diff --git a/Documentation/Changes b/Documentation/Changes index 57542bc25edd..b37600754762 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -63,7 +63,7 @@ o PPP 2.4.0 # pppd --version o isdn4k-utils 3.1pre1 # isdnctrl 2>&1|grep version o nfs-utils 1.0.5 # showmount --version o procps 3.2.0 # ps --version -o oprofile 0.5.3 # oprofiled --version +o oprofile 0.9 # oprofiled --version Kernel compilation ================== diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 87da3478fada..fa3e29ad8a46 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -49,7 +49,7 @@ installmandocs: mandocs KERNELDOC = scripts/kernel-doc DOCPROC = scripts/basic/docproc -XMLTOFLAGS = -m Documentation/DocBook/stylesheet.xsl +XMLTOFLAGS = -m $(srctree)/Documentation/DocBook/stylesheet.xsl #XMLTOFLAGS += --skip-validation ### diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index bb6a0106be11..d650ce36485f 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -266,7 +266,7 @@ X!Ekernel/module.c Hardware Interfaces Interrupt Handling -!Iarch/i386/kernel/irq.c +!Ikernel/irq/manage.c Resources Management diff --git a/Documentation/DocBook/stylesheet.xsl b/Documentation/DocBook/stylesheet.xsl index e14c21dda403..64be9f7ee3bb 100644 --- a/Documentation/DocBook/stylesheet.xsl +++ b/Documentation/DocBook/stylesheet.xsl @@ -2,4 +2,5 @@ 1 ansi +80 diff --git a/Documentation/IPMI.txt b/Documentation/IPMI.txt index 90d10e708ca3..84d3d4d10c17 100644 --- a/Documentation/IPMI.txt +++ b/Documentation/IPMI.txt @@ -25,9 +25,10 @@ subject and I can't cover it all here! Configuration ------------- -The LinuxIPMI driver is modular, which means you have to pick several +The Linux IPMI driver is modular, which means you have to pick several things to have it work right depending on your hardware. Most of -these are available in the 'Character Devices' menu. +these are available in the 'Character Devices' menu then the IPMI +menu. No matter what, you must pick 'IPMI top-level message handler' to use IPMI. What you do beyond that depends on your needs and hardware. @@ -35,33 +36,30 @@ IPMI. What you do beyond that depends on your needs and hardware. The message handler does not provide any user-level interfaces. Kernel code (like the watchdog) can still use it. If you need access from userland, you need to select 'Device interface for IPMI' if you -want access through a device driver. Another interface is also -available, you may select 'IPMI sockets' in the 'Networking Support' -main menu. This provides a socket interface to IPMI. You may select -both of these at the same time, they will both work together. +want access through a device driver. -The driver interface depends on your hardware. If you have a board -with a standard interface (These will generally be either "KCS", -"SMIC", or "BT", consult your hardware manual), choose the 'IPMI SI -handler' option. A driver also exists for direct I2C access to the -IPMI management controller. Some boards support this, but it is -unknown if it will work on every board. For this, choose 'IPMI SMBus -handler', but be ready to try to do some figuring to see if it will -work. - -There is also a KCS-only driver interface supplied, but it is -depracated in favor of the SI interface. +The driver interface depends on your hardware. If your system +properly provides the SMBIOS info for IPMI, the driver will detect it +and just work. If you have a board with a standard interface (These +will generally be either "KCS", "SMIC", or "BT", consult your hardware +manual), choose the 'IPMI SI handler' option. A driver also exists +for direct I2C access to the IPMI management controller. Some boards +support this, but it is unknown if it will work on every board. For +this, choose 'IPMI SMBus handler', but be ready to try to do some +figuring to see if it will work on your system if the SMBIOS/APCI +information is wrong or not present. It is fairly safe to have both +these enabled and let the drivers auto-detect what is present. You should generally enable ACPI on your system, as systems with IPMI -should have ACPI tables describing them. +can have ACPI tables describing them. If you have a standard interface and the board manufacturer has done their job correctly, the IPMI controller should be automatically -detect (via ACPI or SMBIOS tables) and should just work. Sadly, many -boards do not have this information. The driver attempts standard -defaults, but they may not work. If you fall into this situation, you -need to read the section below named 'The SI Driver' on how to -hand-configure your system. +detected (via ACPI or SMBIOS tables) and should just work. Sadly, +many boards do not have this information. The driver attempts +standard defaults, but they may not work. If you fall into this +situation, you need to read the section below named 'The SI Driver' or +"The SMBus Driver" on how to hand-configure your system. IPMI defines a standard watchdog timer. You can enable this with the 'IPMI Watchdog Timer' config option. If you compile the driver into @@ -73,6 +71,18 @@ closed (by default it is disabled on close). Go into the 'Watchdog Cards' menu, enable 'Watchdog Timer Support', and enable the option 'Disable watchdog shutdown on close'. +IPMI systems can often be powered off using IPMI commands. Select +'IPMI Poweroff' to do this. The driver will auto-detect if the system +can be powered off by IPMI. It is safe to enable this even if your +system doesn't support this option. This works on ATCA systems, the +Radisys CPI1 card, and any IPMI system that supports standard chassis +management commands. + +If you want the driver to put an event into the event log on a panic, +enable the 'Generate a panic event to all BMCs on a panic' option. If +you want the whole panic string put into the event log using OEM +events, enable the 'Generate OEM events containing the panic string' +option. Basic Design ------------ @@ -80,7 +90,7 @@ Basic Design The Linux IPMI driver is designed to be very modular and flexible, you only need to take the pieces you need and you can use it in many different ways. Because of that, it's broken into many chunks of -code. These chunks are: +code. These chunks (by module name) are: ipmi_msghandler - This is the central piece of software for the IPMI system. It handles all messages, message timing, and responses. The @@ -93,18 +103,26 @@ ipmi_devintf - This provides a userland IOCTL interface for the IPMI driver, each open file for this device ties in to the message handler as an IPMI user. -ipmi_si - A driver for various system interfaces. This supports -KCS, SMIC, and may support BT in the future. Unless you have your own -custom interface, you probably need to use this. +ipmi_si - A driver for various system interfaces. This supports KCS, +SMIC, and BT interfaces. Unless you have an SMBus interface or your +own custom interface, you probably need to use this. ipmi_smb - A driver for accessing BMCs on the SMBus. It uses the I2C kernel driver's SMBus interfaces to send and receive IPMI messages over the SMBus. -af_ipmi - A network socket interface to IPMI. This doesn't take up -a character device in your system. +ipmi_watchdog - IPMI requires systems to have a very capable watchdog +timer. This driver implements the standard Linux watchdog timer +interface on top of the IPMI message handler. -Note that the KCS-only interface ahs been removed. +ipmi_poweroff - Some systems support the ability to be turned off via +IPMI commands. + +These are all individually selectable via configuration options. + +Note that the KCS-only interface has been removed. The af_ipmi driver +is no longer supported and has been removed because it was impossible +to do 32 bit emulation on 64-bit kernels with it. Much documentation for the interface is in the include files. The IPMI include files are: @@ -424,7 +442,7 @@ at module load time (for a module) with: modprobe ipmi_smb.o addr=,[,,[,...]] dbg=,... - [defaultprobe=0] [dbg_probe=1] + [defaultprobe=1] [dbg_probe=1] The addresses are specified in pairs, the first is the adapter ID and the second is the I2C address on that adapter. @@ -532,3 +550,67 @@ Once you open the watchdog timer, you must write a 'V' character to the device to close it, or the timer will not stop. This is a new semantic for the driver, but makes it consistent with the rest of the watchdog drivers in Linux. + + +Panic Timeouts +-------------- + +The OpenIPMI driver supports the ability to put semi-custom and custom +events in the system event log if a panic occurs. if you enable the +'Generate a panic event to all BMCs on a panic' option, you will get +one event on a panic in a standard IPMI event format. If you enable +the 'Generate OEM events containing the panic string' option, you will +also get a bunch of OEM events holding the panic string. + + +The field settings of the events are: +* Generator ID: 0x21 (kernel) +* EvM Rev: 0x03 (this event is formatting in IPMI 1.0 format) +* Sensor Type: 0x20 (OS critical stop sensor) +* Sensor #: The first byte of the panic string (0 if no panic string) +* Event Dir | Event Type: 0x6f (Assertion, sensor-specific event info) +* Event Data 1: 0xa1 (Runtime stop in OEM bytes 2 and 3) +* Event data 2: second byte of panic string +* Event data 3: third byte of panic string +See the IPMI spec for the details of the event layout. This event is +always sent to the local management controller. It will handle routing +the message to the right place + +Other OEM events have the following format: +Record ID (bytes 0-1): Set by the SEL. +Record type (byte 2): 0xf0 (OEM non-timestamped) +byte 3: The slave address of the card saving the panic +byte 4: A sequence number (starting at zero) +The rest of the bytes (11 bytes) are the panic string. If the panic string +is longer than 11 bytes, multiple messages will be sent with increasing +sequence numbers. + +Because you cannot send OEM events using the standard interface, this +function will attempt to find an SEL and add the events there. It +will first query the capabilities of the local management controller. +If it has an SEL, then they will be stored in the SEL of the local +management controller. If not, and the local management controller is +an event generator, the event receiver from the local management +controller will be queried and the events sent to the SEL on that +device. Otherwise, the events go nowhere since there is nowhere to +send them. + + +Poweroff +-------- + +If the poweroff capability is selected, the IPMI driver will install +a shutdown function into the standard poweroff function pointer. This +is in the ipmi_poweroff module. When the system requests a powerdown, +it will send the proper IPMI commands to do this. This is supported on +several platforms. + +There is a module parameter named "poweroff_control" that may either be zero +(do a power down) or 2 (do a power cycle, power the system off, then power +it on in a few seconds). Setting ipmi_poweroff.poweroff_control=x will do +the same thing on the kernel command line. The parameter is also available +via the proc filesystem in /proc/ipmi/poweroff_control. Note that if the +system does not support power cycling, it will always to the power off. + +Note that if you have ACPI enabled, the system will prefer using ACPI to +power off. diff --git a/Documentation/basic_profiling.txt b/Documentation/basic_profiling.txt index 65e3dc2d4437..8764e9f70821 100644 --- a/Documentation/basic_profiling.txt +++ b/Documentation/basic_profiling.txt @@ -27,9 +27,13 @@ dump output readprofile -m /boot/System.map > captured_profile Oprofile -------- -Get the source (I use 0.8) from http://oprofile.sourceforge.net/ -and add "idle=poll" to the kernel command line + +Get the source (see Changes for required version) from +http://oprofile.sourceforge.net/ and add "idle=poll" to the kernel command +line. + Configure with CONFIG_PROFILING=y and CONFIG_OPROFILE=y & reboot on new kernel + ./configure --with-kernel-support make install @@ -46,7 +50,7 @@ start opcontrol --start stop opcontrol --stop dump output opreport > output_file -To only report on the kernel, run opreport /boot/vmlinux > output_file +To only report on the kernel, run opreport -l /boot/vmlinux > output_file A reset is needed to clear old statistics, which survive a reboot. diff --git a/Documentation/dontdiff b/Documentation/dontdiff index 9a33bb94f74f..d4fda25db868 100644 --- a/Documentation/dontdiff +++ b/Documentation/dontdiff @@ -111,6 +111,7 @@ mkdep mktables modpost modversions.h* +offset.h offsets.h oui.c* parse.c* diff --git a/Documentation/dvb/README.dibusb b/Documentation/dvb/README.dvb-usb similarity index 57% rename from Documentation/dvb/README.dibusb rename to Documentation/dvb/README.dvb-usb index 7a9e958513f3..c7ed01b9f8f4 100644 --- a/Documentation/dvb/README.dibusb +++ b/Documentation/dvb/README.dvb-usb @@ -1,16 +1,40 @@ -Documentation for dib3000* frontend drivers and dibusb device driver -==================================================================== +Documentation for dvb-usb-framework module and its devices -Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de), +Idea behind the dvb-usb-framework +================================= -dibusb and dib3000mb/mc drivers based on GPL code, which has +In March 2005 I got the new Twinhan USB2.0 DVB-T device. They provided specs and a firmware. -Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) +Quite keen I wanted to put the driver (with some quirks of course) into dibusb. +After reading some specs and doing some USB snooping, it realized, that the +dibusb-driver would be a complete mess afterwards. So I decided to do it in a +different way: With the help of a dvb-usb-framework. -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, version 2. +The framework provides generic functions (mostly kernel API calls), such as: +- Transport Stream URB handling in conjunction with dvb-demux-feed-control + (bulk and isoc (TODO) are supported) +- registering the device for the DVB-API +- registering an I2C-adapter if applicable +- remote-control/input-device handling +- firmware requesting and loading (currently just for the Cypress USB + controller) +- other functions/methods which can be shared by several drivers (such as + functions for bulk-control-commands) + +The source code of the particular DVB USB devices does just the communication +with the device via the bus. The connection between the DVB-API-functionality +is done via callbacks, assigned in a static device-description (struct +dvb_usb_device) each device-driver has to have. + +For an example have a look in drivers/media/dvb/dvb-usb/vp7045*. + +Objective is to migrate all the usb-devices (dibusb, cinergyT2, maybe the +ttusb; flexcop-usb already benefits from the generic flexcop-device) to use +the dvb-usb-lib. + +TODO: dynamic enabling and disabling of the pid-filter in regard to number of +feeds requested. Supported devices USB1.1 ======================== @@ -55,22 +79,34 @@ Others: - Grandtec USB DVB-T http://www.grand.com.tw/ -- Avermedia AverTV DVBT USB (2) +- AVerMedia AverTV DVBT USB http://www.avermedia.com/ - DiBcom USB DVB-T reference device (non-public) -Supported devices USB2.0 -======================== -- Twinhan MagicBox II (2) +Supported devices USB2.0-only +============================= +- Twinhan MagicBox II http://www.twinhan.com/product_terrestrial_7.asp -- Hanftek UMT-010 (1) +- TwinhanDTV Alpha + http://www.twinhan.com/product_terrestrial_8.asp + +- DigitalNow TinyUSB 2 DVB-t Receiver + http://www.digitalnow.com.au/DigitalNow%20tinyUSB2%20Specifications.html + +- Hanftek UMT-010 http://www.globalsources.com/si/6008819757082/ProductDetail/Digital-TV/product_id-100046529 -- Typhoon/Yakumo/HAMA DVB-T mobile USB2.0 (1) + +Supported devices USB2.0 and USB1.1 +============================= +- Typhoon/Yakumo/HAMA/Yuan DVB-T mobile USB2.0 http://www.yakumo.de/produkte/index.php?pid=1&ag=DVB-T + http://www.yuan.com.tw/en/products/vdo_ub300.html + http://www.hama.de/portal/articleId*114663/action*2563 + http://www.anubisline.com/english/articlec.asp?id=50502&catid=002 - Artec T1 USB TVBOX (FX2) (2) @@ -81,14 +117,24 @@ Supported devices USB2.0 - DiBcom USB2.0 DVB-T reference device (non-public) -1) It is working almost. +- AVerMedia AverTV A800 DVB-T USB2.0 + +1) It is working almost - work-in-progress. 2) No test reports received yet. +0. History & News: + 2005-04-17 - all dibusb devices ported to make use of the dvb-usb-framework + 2005-04-02 - re-enabled and improved remote control code. + 2005-03-31 - ported the Yakumo/Hama/Typhoon DVB-T USB2.0 device to dvb-usb. + 2005-03-30 - first commit of the dvb-usb-module based on the dibusb-source. First device is a new driver for the + TwinhanDTV Alpha / MagicBox II USB2.0-only DVB-T device. -0. NEWS: + (change from dvb-dibusb to dvb-usb) + 2005-03-28 - added support for the AVerMedia AverTV DVB-T USB2.0 device (Thanks to Glen Harris and Jiun-Kuei Jung, AVerMedia) + 2005-03-14 - added support for the Typhoon/Yakumo/HAMA DVB-T mobile USB2.0 2005-02-11 - added support for the KWorld/ADSTech Instant DVB-T USB2.0. Thanks a lot to Joachim von Caron 2005-02-02 - added support for the Hauppauge Win-TV Nova-T USB2 - 2005-01-31 - distorted streaming is finally gone for USB1.1 devices + 2005-01-31 - distorted streaming is gone for USB1.1 devices 2005-01-13 - moved the mirrored pid_filter_table back to dvb-dibusb - first almost working version for HanfTek UMT-010 - found out, that Yakumo/HAMA/Typhoon are predessors of the HanfTek UMT-010 @@ -99,7 +145,7 @@ Supported devices USB2.0 2004-12-26 - refactored the dibusb-driver, splitted into separate files - i2c-probing enabled 2004-12-06 - possibility for demod i2c-address probing - - new usb IDs (Compro,Artec) + - new usb IDs (Compro, Artec) 2004-11-23 - merged changes from DiB3000MC_ver2.1 - revised the debugging - possibility to deliver the complete TS for USB2.0 @@ -127,8 +173,8 @@ Supported devices USB2.0 CTS Portable (Chinese Television System) 2004-07-08 - firmware-extraction-2.422-problem solved, driver is now working properly with firmware extracted from 2.422 - - #if for 2.6.4 (dvb), compile issue - - changed firmware handling, see vp7041.txt sec 1.1 + - #if for 2.6.4 (dvb), compile issue + - changed firmware handling, see vp7041.txt sec 1.1 2004-07-02 - some tuner modifications, v0.1, cleanups, first public 2004-06-28 - now using the dvb_dmx_swfilter_packets, everything runs fine now @@ -139,38 +185,27 @@ Supported devices USB2.0 2004-05-11 - start writing the driver 1. How to use? -NOTE: This driver was developed using Linux 2.6.6., -it is working with 2.6.7 and above. - -Linux 2.4.x support is not planned, but patches are very welcome. - -NOTE: I'm using Debian testing, so the following explaination (especially -the hotplug-path) needn't match your system, but probably it will :). - -The driver is included in the kernel since Linux 2.6.10. - 1.1. Firmware -The USB driver needs to download a firmware to start working. +Most of the USB drivers need to download a firmware to start working. -You can either use "get_dvb_firmware dibusb" to download the firmware or you -can get it directly via +for USB1.1 (AN2135) you need: dvb-usb-dibusb-5.0.0.11.fw +for USB2.0 HanfTek: dvb-usb-umt-010-02.fw +for USB2.0 DiBcom: dvb-usb-dibusb-6.0.0.8.fw +for USB2.0 AVerMedia AverTV DVB-T USB2: dvb-usb-avertv-a800-01.fw +for USB2.0 TwinhanDTV Alpha/MagicBox II: dvb-usb-vp7045-01.fw -for USB1.1 (AN2135) -http://www.linuxtv.org/downloads/firmware/dvb-dibusb-5.0.0.11.fw +The files can be found on http://www.linuxtv.org/download/firmware/ . -for USB1.1 (AN2235) (a few Artec T1 devices) -http://www.linuxtv.org/downloads/firmware/dvb-dibusb-an2235-1.fw +We do not have the permission (yet) to publish the following firmware-files. +You'll need to extract them from the windows drivers. -for USB2.0 (FX2) Hauppauge, DiBcom -http://www.linuxtv.org/downloads/firmware/dvb-dibusb-6.0.0.5.fw - -for USB2.0 ADSTech/Kworld USB2.0 -http://www.linuxtv.org/downloads/firmware/dvb-dibusb-adstech-usb2-1.fw - -for USB2.0 HanfTek -http://www.linuxtv.org/downloads/firmware/dvb-dibusb-an2235-1.fw +You should be able to use "get_dvb_firmware dvb-usb" to get the firmware: +for USB1.1 (AN2235) (a few Artec T1 devices): dvb-usb-dibusb-an2235-01.fw +for USB2.0 Hauppauge: dvb-usb-nova-t-usb2-01.fw +for USB2.0 ADSTech/Kworld USB2.0: dvb-usb-adstech-usb2-01.fw +for USB2.0 Yakumo/Typhoon/Hama: dvb-usb-dtt200u-01.fw 1.2. Compiling @@ -178,6 +213,9 @@ Since the driver is in the linux kernel, activating the driver in your favorite config-environment should sufficient. I recommend to compile the driver as module. Hotplug does the rest. +If you use dvb-kernel enter the build-2.6 directory run 'make' and 'insmod.sh +load' afterwards. + 1.3. Loading the drivers Hotplug is able to load the driver, when it is needed (because you plugged @@ -188,15 +226,13 @@ from withing the dvb-kernel cvs repository. first have a look, which debug level are available: -modinfo dib3000mb -modinfo dib3000-common -modinfo dib3000mc -modinfo dvb-dibusb +modinfo dvb-usb +modinfo dvb-usb-vp7045 +etc. -modprobe dib3000-common debug= -modprobe dib3000mb debug= -modprobe dib3000mc debug= -modprobe dvb-dibusb debug= +modprobe dvb-usb debug= +modprobe dvb-usb-vp7045 debug= +etc. should do the trick. @@ -204,52 +240,32 @@ When the driver is loaded successfully, the firmware file was in the right place and the device is connected, the "Power"-LED should be turned on. -At this point you should be able to start a dvb-capable application. For myself -I used mplayer, dvbscan, tzap and kaxtv, they are working. Using the device -in vdr is working now also. +At this point you should be able to start a dvb-capable application. I'm use +(t|s)zap, mplayer and dvbscan to test the basics. VDR-xine provides the +long-term test scenario. 2. Known problems and bugs -- Don't remove the USB device while running an DVB application, your system will die. +- Don't remove the USB device while running an DVB application, your system + will go crazy or die most likely. 2.1. Adding support for devices -It is not possible to determine the range of devices based on the DiBcom -reference designs. This is because the reference design of DiBcom can be sold -to thirds, without telling DiBcom (so done with the Twinhan VP7041 and -the HAMA device). - -When you think you have a device like this and the driver does not recognizes it, -please send the ****load*.inf and the ****cap*.inf of the Windows driver to me. - -Sometimes the Vendor or Product ID is identical to the ones of Twinhan, even -though it is not a Twinhan device (e.g. HAMA), then please send me the name -of the device. I will add it to this list in order to make this clear to -others. - -If you are familar with C you can also add the VID and PID of the device to -the dvb-dibusb-core.c-file and create a patch and send it over to me or to -the linux-dvb mailing list, _after_ you have tried compiling and modprobing -it. +TODO 2.2. USB1.1 Bandwidth limitation -Most of the currently supported devices are USB1.1 and thus they have a +A lot of the currently supported devices are USB1.1 and thus they have a maximum bandwidth of about 5-6 MBit/s when connected to a USB2.0 hub. This is not enough for receiving the complete transport stream of a -DVB-T channel (which can be about 16 MBit/s). Normally this is not a +DVB-T channel (which is about 16 MBit/s). Normally this is not a problem, if you only want to watch TV (this does not apply for HDTV), but watching a channel while recording another channel on the same frequency simply does not work very well. This applies to all USB1.1 -DVB-T devices, not just dibusb) - -Update: For the USB1.1 and VDR some work has been done (patches and comments -are still very welcome). Maybe the problem is solved in the meantime because I -now use the dmx_sw_filter function instead of dmx_sw_filter_packet. I hope the -linux-dvb software filter is able to get the best of the garbled TS. +DVB-T devices, not just the dvb-usb-devices) The bug, where the TS is distorted by a heavy usage of the device is gone -definitely. All dibusb-devices I was using (Twinhan, Kworld, DiBcom) are +definitely. All dvb-usb-devices I was using (Twinhan, Kworld, DiBcom) are working like charm now with VDR. Sometimes I even was able to record a channel and watch another one. @@ -258,7 +274,7 @@ and watch another one. Patches, comments and suggestions are very very welcome. 3. Acknowledgements - Amaury Demol (ademol@dibcom.fr) and Francois Kanounnikoff from DiBcom for + Amaury Demol (ademol@dibcom.fr) and Francois Kanounnikoff from DiBcom for providing specs, code and help, on which the dvb-dibusb, dib3000mb and dib3000mc are based. @@ -270,9 +286,16 @@ Patches, comments and suggestions are very very welcome. Bernd Wagner for helping with huge bug reports and discussions. - Gunnar Wittich and Joachim von Caron for their trust for giving me + Gunnar Wittich and Joachim von Caron for their trust for providing root-shells on their machines to implement support for new devices. + Glen Harris for bringing up, that there is a new dibusb-device and Jiun-Kuei + Jung from AVerMedia who kindly provided a special firmware to get the device + up and running in Linux. + + Jennifer Chen, Jeff and Jack from Twinhan for kindly supporting by + writing the vp7045-driver. + Some guys on the linux-dvb mailing list for encouraging me Peter Schildmann >peter.schildmann-nospam-at-web.de< for his @@ -282,4 +305,4 @@ Patches, comments and suggestions are very very welcome. Ulf Hermenau for helping me out with traditional chinese. André Smoktun and Christian Frömmel for supporting me with - hardware and listening to my problems very patient + hardware and listening to my problems very patient. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 26414bc87c65..77511af45362 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -66,6 +66,14 @@ Who: Paul E. McKenney --------------------------- +What: remove verify_area() +When: July 2006 +Files: Various uaccess.h headers. +Why: Deprecated and redundant. access_ok() should be used instead. +Who: Jesper Juhl + +--------------------------- + What: IEEE1394 Audio and Music Data Transmission Protocol driver, Connection Management Procedures driver When: November 2005 @@ -86,6 +94,16 @@ Who: Jody McIntyre --------------------------- +What: register_serial/unregister_serial +When: December 2005 +Why: This interface does not allow serial ports to be registered against + a struct device, and as such does not allow correct power management + of such ports. 8250-based ports should use serial8250_register_port + and serial8250_unregister_port instead. +Who: Russell King + +--------------------------- + What: i2c sysfs name change: in1_ref, vid deprecated in favour of cpu0_vid When: November 2005 Files: drivers/i2c/chips/adm1025.c, drivers/i2c/chips/adm1026.c diff --git a/Documentation/filesystems/ext2.txt b/Documentation/filesystems/ext2.txt index b5cb9110cc6b..d16334ec48ba 100644 --- a/Documentation/filesystems/ext2.txt +++ b/Documentation/filesystems/ext2.txt @@ -58,6 +58,8 @@ noacl Don't support POSIX ACLs. nobh Do not attach buffer_heads to file pagecache. +xip Use execute in place (no caching) if possible + grpquota,noquota,quota,usrquota Quota options are silently ignored by ext2. diff --git a/Documentation/filesystems/xip.txt b/Documentation/filesystems/xip.txt new file mode 100644 index 000000000000..6c0cef10eb4d --- /dev/null +++ b/Documentation/filesystems/xip.txt @@ -0,0 +1,67 @@ +Execute-in-place for file mappings +---------------------------------- + +Motivation +---------- +File mappings are performed by mapping page cache pages to userspace. In +addition, read&write type file operations also transfer data from/to the page +cache. + +For memory backed storage devices that use the block device interface, the page +cache pages are in fact copies of the original storage. Various approaches +exist to work around the need for an extra copy. The ramdisk driver for example +does read the data into the page cache, keeps a reference, and discards the +original data behind later on. + +Execute-in-place solves this issue the other way around: instead of keeping +data in the page cache, the need to have a page cache copy is eliminated +completely. With execute-in-place, read&write type operations are performed +directly from/to the memory backed storage device. For file mappings, the +storage device itself is mapped directly into userspace. + +This implementation was initialy written for shared memory segments between +different virtual machines on s390 hardware to allow multiple machines to +share the same binaries and libraries. + +Implementation +-------------- +Execute-in-place is implemented in three steps: block device operation, +address space operation, and file operations. + +A block device operation named direct_access is used to retrieve a +reference (pointer) to a block on-disk. The reference is supposed to be +cpu-addressable, physical address and remain valid until the release operation +is performed. A struct block_device reference is used to address the device, +and a sector_t argument is used to identify the individual block. As an +alternative, memory technology devices can be used for this. + +The block device operation is optional, these block devices support it as of +today: +- dcssblk: s390 dcss block device driver + +An address space operation named get_xip_page is used to retrieve reference +to a struct page. To address the target page, a reference to an address_space, +and a sector number is provided. A 3rd argument indicates whether the +function should allocate blocks if needed. + +This address space operation is mutually exclusive with readpage&writepage that +do page cache read/write operations. +The following filesystems support it as of today: +- ext2: the second extended filesystem, see Documentation/filesystems/ext2.txt + +A set of file operations that do utilize get_xip_page can be found in +mm/filemap_xip.c . The following file operation implementations are provided: +- aio_read/aio_write +- readv/writev +- sendfile + +The generic file operations do_sync_read/do_sync_write can be used to implement +classic synchronous IO calls. + +Shortcomings +------------ +This implementation is limited to storage devices that are cpu addressable at +all times (no highmem or such). It works well on rom/ram, but enhancements are +needed to make it work with flash in read+write mode. +Putting the Linux kernel and/or its modules on a xip filesystem does not mean +they are not copied. diff --git a/Documentation/keys.txt b/Documentation/keys.txt index 36d80aeeaf28..0321ded4b9ae 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -22,6 +22,7 @@ This document has the following sections: - New procfs files - Userspace system call interface - Kernel services + - Notes on accessing payload contents - Defining a key type - Request-key callback service - Key access filesystem @@ -45,27 +46,26 @@ Each key has a number of attributes: - State. - (*) Each key is issued a serial number of type key_serial_t that is unique - for the lifetime of that key. All serial numbers are positive non-zero - 32-bit integers. + (*) Each key is issued a serial number of type key_serial_t that is unique for + the lifetime of that key. All serial numbers are positive non-zero 32-bit + integers. Userspace programs can use a key's serial numbers as a way to gain access to it, subject to permission checking. (*) Each key is of a defined "type". Types must be registered inside the - kernel by a kernel service (such as a filesystem) before keys of that - type can be added or used. Userspace programs cannot define new types - directly. + kernel by a kernel service (such as a filesystem) before keys of that type + can be added or used. Userspace programs cannot define new types directly. - Key types are represented in the kernel by struct key_type. This defines - a number of operations that can be performed on a key of that type. + Key types are represented in the kernel by struct key_type. This defines a + number of operations that can be performed on a key of that type. Should a type be removed from the system, all the keys of that type will be invalidated. (*) Each key has a description. This should be a printable string. The key - type provides an operation to perform a match between the description on - a key and a criterion string. + type provides an operation to perform a match between the description on a + key and a criterion string. (*) Each key has an owner user ID, a group ID and a permissions mask. These are used to control what a process may do to a key from userspace, and @@ -74,10 +74,10 @@ Each key has a number of attributes: (*) Each key can be set to expire at a specific time by the key type's instantiation function. Keys can also be immortal. - (*) Each key can have a payload. This is a quantity of data that represent - the actual "key". In the case of a keyring, this is a list of keys to - which the keyring links; in the case of a user-defined key, it's an - arbitrary blob of data. + (*) Each key can have a payload. This is a quantity of data that represent the + actual "key". In the case of a keyring, this is a list of keys to which + the keyring links; in the case of a user-defined key, it's an arbitrary + blob of data. Having a payload is not required; and the payload can, in fact, just be a value stored in the struct key itself. @@ -92,8 +92,8 @@ Each key has a number of attributes: (*) Each key can be in one of a number of basic states: - (*) Uninstantiated. The key exists, but does not have any data - attached. Keys being requested from userspace will be in this state. + (*) Uninstantiated. The key exists, but does not have any data attached. + Keys being requested from userspace will be in this state. (*) Instantiated. This is the normal state. The key is fully formed, and has data attached. @@ -140,10 +140,10 @@ The key service provides a number of features besides keys: clone, fork, vfork or execve occurs. A new keyring is created only when required. - The process-specific keyring is replaced with an empty one in the child - on clone, fork, vfork unless CLONE_THREAD is supplied, in which case it - is shared. execve also discards the process's process keyring and creates - a new one. + The process-specific keyring is replaced with an empty one in the child on + clone, fork, vfork unless CLONE_THREAD is supplied, in which case it is + shared. execve also discards the process's process keyring and creates a + new one. The session-specific keyring is persistent across clone, fork, vfork and execve, even when the latter executes a set-UID or set-GID binary. A @@ -177,11 +177,11 @@ The key service provides a number of features besides keys: If a system call that modifies a key or keyring in some way would put the user over quota, the operation is refused and error EDQUOT is returned. - (*) There's a system call interface by which userspace programs can create - and manipulate keys and keyrings. + (*) There's a system call interface by which userspace programs can create and + manipulate keys and keyrings. - (*) There's a kernel interface by which services can register types and - search for keys. + (*) There's a kernel interface by which services can register types and search + for keys. (*) There's a way for the a search done from the kernel to call back to userspace to request a key that can't be found in a process's keyrings. @@ -194,9 +194,9 @@ The key service provides a number of features besides keys: KEY ACCESS PERMISSIONS ====================== -Keys have an owner user ID, a group access ID, and a permissions mask. The -mask has up to eight bits each for user, group and other access. Only five of -each set of eight bits are defined. These permissions granted are: +Keys have an owner user ID, a group access ID, and a permissions mask. The mask +has up to eight bits each for user, group and other access. Only five of each +set of eight bits are defined. These permissions granted are: (*) View @@ -210,8 +210,8 @@ each set of eight bits are defined. These permissions granted are: (*) Write - This permits a key's payload to be instantiated or updated, or it allows - a link to be added to or removed from a keyring. + This permits a key's payload to be instantiated or updated, or it allows a + link to be added to or removed from a keyring. (*) Search @@ -238,8 +238,8 @@ about the status of the key service: (*) /proc/keys This lists all the keys on the system, giving information about their - type, description and permissions. The payload of the key is not - available this way: + type, description and permissions. The payload of the key is not available + this way: SERIAL FLAGS USAGE EXPY PERM UID GID TYPE DESCRIPTION: SUMMARY 00000001 I----- 39 perm 1f0000 0 0 keyring _uid_ses.0: 1/4 @@ -318,21 +318,21 @@ The main syscalls are: If a key of the same type and description as that proposed already exists in the keyring, this will try to update it with the given payload, or it will return error EEXIST if that function is not supported by the key - type. The process must also have permission to write to the key to be - able to update it. The new key will have all user permissions granted and - no group or third party permissions. + type. The process must also have permission to write to the key to be able + to update it. The new key will have all user permissions granted and no + group or third party permissions. - Otherwise, this will attempt to create a new key of the specified type - and description, and to instantiate it with the supplied payload and - attach it to the keyring. In this case, an error will be generated if the - process does not have permission to write to the keyring. + Otherwise, this will attempt to create a new key of the specified type and + description, and to instantiate it with the supplied payload and attach it + to the keyring. In this case, an error will be generated if the process + does not have permission to write to the keyring. The payload is optional, and the pointer can be NULL if not required by the type. The payload is plen in size, and plen can be zero for an empty payload. - A new keyring can be generated by setting type "keyring", the keyring - name as the description (or NULL) and setting the payload to NULL. + A new keyring can be generated by setting type "keyring", the keyring name + as the description (or NULL) and setting the payload to NULL. User defined keys can be created by specifying type "user". It is recommended that a user defined key's description by prefixed with a type @@ -369,9 +369,9 @@ The keyctl syscall functions are: key_serial_t keyctl(KEYCTL_GET_KEYRING_ID, key_serial_t id, int create); - The special key specified by "id" is looked up (with the key being - created if necessary) and the ID of the key or keyring thus found is - returned if it exists. + The special key specified by "id" is looked up (with the key being created + if necessary) and the ID of the key or keyring thus found is returned if + it exists. If the key does not yet exist, the key will be created if "create" is non-zero; and the error ENOKEY will be returned if "create" is zero. @@ -402,8 +402,8 @@ The keyctl syscall functions are: This will try to update the specified key with the given payload, or it will return error EOPNOTSUPP if that function is not supported by the key - type. The process must also have permission to write to the key to be - able to update it. + type. The process must also have permission to write to the key to be able + to update it. The payload is of length plen, and may be absent or empty as for add_key(). @@ -422,8 +422,8 @@ The keyctl syscall functions are: long keyctl(KEYCTL_CHOWN, key_serial_t key, uid_t uid, gid_t gid); - This function permits a key's owner and group ID to be changed. Either - one of uid or gid can be set to -1 to suppress that change. + This function permits a key's owner and group ID to be changed. Either one + of uid or gid can be set to -1 to suppress that change. Only the superuser can change a key's owner to something other than the key's current owner. Similarly, only the superuser can change a key's @@ -484,12 +484,12 @@ The keyctl syscall functions are: long keyctl(KEYCTL_LINK, key_serial_t keyring, key_serial_t key); - This function creates a link from the keyring to the key. The process - must have write permission on the keyring and must have link permission - on the key. + This function creates a link from the keyring to the key. The process must + have write permission on the keyring and must have link permission on the + key. - Should the keyring not be a keyring, error ENOTDIR will result; and if - the keyring is full, error ENFILE will result. + Should the keyring not be a keyring, error ENOTDIR will result; and if the + keyring is full, error ENFILE will result. The link procedure checks the nesting of the keyrings, returning ELOOP if it appears to deep or EDEADLK if the link would introduce a cycle. @@ -503,8 +503,8 @@ The keyctl syscall functions are: specified key, and removes it if found. Subsequent links to that key are ignored. The process must have write permission on the keyring. - If the keyring is not a keyring, error ENOTDIR will result; and if the - key is not present, error ENOENT will be the result. + If the keyring is not a keyring, error ENOTDIR will result; and if the key + is not present, error ENOENT will be the result. (*) Search a keyring tree for a key: @@ -513,9 +513,9 @@ The keyctl syscall functions are: const char *type, const char *description, key_serial_t dest_keyring); - This searches the keyring tree headed by the specified keyring until a - key is found that matches the type and description criteria. Each keyring - is checked for keys before recursion into its children occurs. + This searches the keyring tree headed by the specified keyring until a key + is found that matches the type and description criteria. Each keyring is + checked for keys before recursion into its children occurs. The process must have search permission on the top level keyring, or else error EACCES will result. Only keyrings that the process has search @@ -549,8 +549,8 @@ The keyctl syscall functions are: As much of the data as can be fitted into the buffer will be copied to userspace if the buffer pointer is not NULL. - On a successful return, the function will always return the amount of - data available rather than the amount copied. + On a successful return, the function will always return the amount of data + available rather than the amount copied. (*) Instantiate a partially constructed key. @@ -568,8 +568,8 @@ The keyctl syscall functions are: it, and the key must be uninstantiated. If a keyring is specified (non-zero), the key will also be linked into - that keyring, however all the constraints applying in KEYCTL_LINK apply - in this case too. + that keyring, however all the constraints applying in KEYCTL_LINK apply in + this case too. The payload and plen arguments describe the payload data as for add_key(). @@ -587,8 +587,39 @@ The keyctl syscall functions are: it, and the key must be uninstantiated. If a keyring is specified (non-zero), the key will also be linked into - that keyring, however all the constraints applying in KEYCTL_LINK apply - in this case too. + that keyring, however all the constraints applying in KEYCTL_LINK apply in + this case too. + + + (*) Set the default request-key destination keyring. + + long keyctl(KEYCTL_SET_REQKEY_KEYRING, int reqkey_defl); + + This sets the default keyring to which implicitly requested keys will be + attached for this thread. reqkey_defl should be one of these constants: + + CONSTANT VALUE NEW DEFAULT KEYRING + ====================================== ====== ======================= + KEY_REQKEY_DEFL_NO_CHANGE -1 No change + KEY_REQKEY_DEFL_DEFAULT 0 Default[1] + KEY_REQKEY_DEFL_THREAD_KEYRING 1 Thread keyring + KEY_REQKEY_DEFL_PROCESS_KEYRING 2 Process keyring + KEY_REQKEY_DEFL_SESSION_KEYRING 3 Session keyring + KEY_REQKEY_DEFL_USER_KEYRING 4 User keyring + KEY_REQKEY_DEFL_USER_SESSION_KEYRING 5 User session keyring + KEY_REQKEY_DEFL_GROUP_KEYRING 6 Group keyring + + The old default will be returned if successful and error EINVAL will be + returned if reqkey_defl is not one of the above values. + + The default keyring can be overridden by the keyring indicated to the + request_key() system call. + + Note that this setting is inherited across fork/exec. + + [1] The default default is: the thread keyring if there is one, otherwise + the process keyring if there is one, otherwise the session keyring if + there is one, otherwise the user default session keyring. =============== @@ -601,17 +632,14 @@ be broken down into two areas: keys and key types. Dealing with keys is fairly straightforward. Firstly, the kernel service registers its type, then it searches for a key of that type. It should retain the key as long as it has need of it, and then it should release it. For a -filesystem or device file, a search would probably be performed during the -open call, and the key released upon close. How to deal with conflicting keys -due to two different users opening the same file is left to the filesystem -author to solve. +filesystem or device file, a search would probably be performed during the open +call, and the key released upon close. How to deal with conflicting keys due to +two different users opening the same file is left to the filesystem author to +solve. -When accessing a key's payload data, key->lock should be at least read locked, -or else the data may be changed by an update being performed from userspace -whilst the driver or filesystem is trying to access it. If no update method is -supplied, then the key's payload may be accessed without holding a lock as -there is no way to change it, provided it can be guaranteed that the key's -type definition won't go away. +When accessing a key's payload contents, certain precautions must be taken to +prevent access vs modification races. See the section "Notes on accessing +payload contents" for more information. (*) To search for a key, call: @@ -629,6 +657,9 @@ type definition won't go away. Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be returned. + If successful, the key will have been attached to the default keyring for + implicitly obtained request-key keys, as set by KEYCTL_SET_REQKEY_KEYRING. + (*) When it is no longer required, the key should be released using: @@ -690,6 +721,54 @@ type definition won't go away. void unregister_key_type(struct key_type *type); +=================================== +NOTES ON ACCESSING PAYLOAD CONTENTS +=================================== + +The simplest payload is just a number in key->payload.value. In this case, +there's no need to indulge in RCU or locking when accessing the payload. + +More complex payload contents must be allocated and a pointer to them set in +key->payload.data. One of the following ways must be selected to access the +data: + + (1) Unmodifyable key type. + + If the key type does not have a modify method, then the key's payload can + be accessed without any form of locking, provided that it's known to be + instantiated (uninstantiated keys cannot be "found"). + + (2) The key's semaphore. + + The semaphore could be used to govern access to the payload and to control + the payload pointer. It must be write-locked for modifications and would + have to be read-locked for general access. The disadvantage of doing this + is that the accessor may be required to sleep. + + (3) RCU. + + RCU must be used when the semaphore isn't already held; if the semaphore + is held then the contents can't change under you unexpectedly as the + semaphore must still be used to serialise modifications to the key. The + key management code takes care of this for the key type. + + However, this means using: + + rcu_read_lock() ... rcu_dereference() ... rcu_read_unlock() + + to read the pointer, and: + + rcu_dereference() ... rcu_assign_pointer() ... call_rcu() + + to set the pointer and dispose of the old contents after a grace period. + Note that only the key type should ever modify a key's payload. + + Furthermore, an RCU controlled payload must hold a struct rcu_head for the + use of call_rcu() and, if the payload is of variable size, the length of + the payload. key->datalen cannot be relied upon to be consistent with the + payload just dereferenced if the key's semaphore is not held. + + =================== DEFINING A KEY TYPE =================== @@ -717,15 +796,15 @@ The structure has a number of fields, some of which are mandatory: int key_payload_reserve(struct key *key, size_t datalen); - With the revised data length. Error EDQUOT will be returned if this is - not viable. + With the revised data length. Error EDQUOT will be returned if this is not + viable. (*) int (*instantiate)(struct key *key, const void *data, size_t datalen); This method is called to attach a payload to a key during construction. - The payload attached need not bear any relation to the data passed to - this function. + The payload attached need not bear any relation to the data passed to this + function. If the amount of data attached to the key differs from the size in keytype->def_datalen, then key_payload_reserve() should be called. @@ -734,38 +813,47 @@ The structure has a number of fields, some of which are mandatory: The fact that KEY_FLAG_INSTANTIATED is not set in key->flags prevents anything else from gaining access to the key. - This method may sleep if it wishes. + It is safe to sleep in this method. (*) int (*duplicate)(struct key *key, const struct key *source); If this type of key can be duplicated, then this method should be - provided. It is called to copy the payload attached to the source into - the new key. The data length on the new key will have been updated and - the quota adjusted already. + provided. It is called to copy the payload attached to the source into the + new key. The data length on the new key will have been updated and the + quota adjusted already. This method will be called with the source key's semaphore read-locked to - prevent its payload from being changed. It is safe to sleep here. + prevent its payload from being changed, thus RCU constraints need not be + applied to the source key. + + This method does not have to lock the destination key in order to attach a + payload. The fact that KEY_FLAG_INSTANTIATED is not set in key->flags + prevents anything else from gaining access to the key. + + It is safe to sleep in this method. (*) int (*update)(struct key *key, const void *data, size_t datalen); - If this type of key can be updated, then this method should be - provided. It is called to update a key's payload from the blob of data - provided. + If this type of key can be updated, then this method should be provided. + It is called to update a key's payload from the blob of data provided. key_payload_reserve() should be called if the data length might change - before any changes are actually made. Note that if this succeeds, the - type is committed to changing the key because it's already been altered, - so all memory allocation must be done first. + before any changes are actually made. Note that if this succeeds, the type + is committed to changing the key because it's already been altered, so all + memory allocation must be done first. - key_payload_reserve() should be called with the key->lock write locked, - and the changes to the key's attached payload should be made before the - key is locked. + The key will have its semaphore write-locked before this method is called, + but this only deters other writers; any changes to the key's payload must + be made under RCU conditions, and call_rcu() must be used to dispose of + the old payload. - The key will have its semaphore write-locked before this method is - called. Any changes to the key should be made with the key's rwlock - write-locked also. It is safe to sleep here. + key_payload_reserve() should be called before the changes are made, but + after all allocations and other potentially failing function calls are + made. + + It is safe to sleep in this method. (*) int (*match)(const struct key *key, const void *desc); @@ -782,12 +870,12 @@ The structure has a number of fields, some of which are mandatory: (*) void (*destroy)(struct key *key); - This method is optional. It is called to discard the payload data on a - key when it is being destroyed. + This method is optional. It is called to discard the payload data on a key + when it is being destroyed. - This method does not need to lock the key; it can consider the key as - being inaccessible. Note that the key's type may have changed before this - function is called. + This method does not need to lock the key to access the payload; it can + consider the key as being inaccessible at this time. Note that the key's + type may have been changed before this function is called. It is not safe to sleep in this method; the caller may hold spinlocks. @@ -797,26 +885,31 @@ The structure has a number of fields, some of which are mandatory: This method is optional. It is called during /proc/keys reading to summarise a key's description and payload in text form. - This method will be called with the key's rwlock read-locked. This will - prevent the key's payload and state changing; also the description should - not change. This also means it is not safe to sleep in this method. + This method will be called with the RCU read lock held. rcu_dereference() + should be used to read the payload pointer if the payload is to be + accessed. key->datalen cannot be trusted to stay consistent with the + contents of the payload. + + The description will not change, though the key's state may. + + It is not safe to sleep in this method; the RCU read lock is held by the + caller. (*) long (*read)(const struct key *key, char __user *buffer, size_t buflen); This method is optional. It is called by KEYCTL_READ to translate the - key's payload into something a blob of data for userspace to deal - with. Ideally, the blob should be in the same format as that passed in to - the instantiate and update methods. + key's payload into something a blob of data for userspace to deal with. + Ideally, the blob should be in the same format as that passed in to the + instantiate and update methods. If successful, the blob size that could be produced should be returned rather than the size copied. - This method will be called with the key's semaphore read-locked. This - will prevent the key's payload changing. It is not necessary to also - read-lock key->lock when accessing the key's payload. It is safe to sleep - in this method, such as might happen when the userspace buffer is - accessed. + This method will be called with the key's semaphore read-locked. This will + prevent the key's payload changing. It is not necessary to use RCU locking + when accessing the key's payload. It is safe to sleep in this method, such + as might happen when the userspace buffer is accessed. ============================ @@ -853,8 +946,8 @@ If it returns with the key remaining in the unconstructed state, the key will be marked as being negative, it will be added to the session keyring, and an error will be returned to the key requestor. -Supplementary information may be provided from whoever or whatever invoked -this service. This will be passed as the parameter. If no such +Supplementary information may be provided from whoever or whatever invoked this +service. This will be passed as the parameter. If no such information was made available, then "-" will be passed as this parameter instead. diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index a2c893a7475d..ab65714d95fc 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -304,57 +304,6 @@ tcp_low_latency - BOOLEAN changed would be a Beowulf compute cluster. Default: 0 -tcp_westwood - BOOLEAN - Enable TCP Westwood+ congestion control algorithm. - TCP Westwood+ is a sender-side only modification of the TCP Reno - protocol stack that optimizes the performance of TCP congestion - control. It is based on end-to-end bandwidth estimation to set - congestion window and slow start threshold after a congestion - episode. Using this estimation, TCP Westwood+ adaptively sets a - slow start threshold and a congestion window which takes into - account the bandwidth used at the time congestion is experienced. - TCP Westwood+ significantly increases fairness wrt TCP Reno in - wired networks and throughput over wireless links. - Default: 0 - -tcp_vegas_cong_avoid - BOOLEAN - Enable TCP Vegas congestion avoidance algorithm. - TCP Vegas is a sender-side only change to TCP that anticipates - the onset of congestion by estimating the bandwidth. TCP Vegas - adjusts the sending rate by modifying the congestion - window. TCP Vegas should provide less packet loss, but it is - not as aggressive as TCP Reno. - Default:0 - -tcp_bic - BOOLEAN - Enable BIC TCP congestion control algorithm. - BIC-TCP is a sender-side only change that ensures a linear RTT - fairness under large windows while offering both scalability and - bounded TCP-friendliness. The protocol combines two schemes - called additive increase and binary search increase. When the - congestion window is large, additive increase with a large - increment ensures linear RTT fairness as well as good - scalability. Under small congestion windows, binary search - increase provides TCP friendliness. - Default: 0 - -tcp_bic_low_window - INTEGER - Sets the threshold window (in packets) where BIC TCP starts to - adjust the congestion window. Below this threshold BIC TCP behaves - the same as the default TCP Reno. - Default: 14 - -tcp_bic_fast_convergence - BOOLEAN - Forces BIC TCP to more quickly respond to changes in congestion - window. Allows two flows sharing the same connection to converge - more rapidly. - Default: 1 - -tcp_default_win_scale - INTEGER - Sets the minimum window scale TCP will negotiate for on all - conections. - Default: 7 - tcp_tso_win_divisor - INTEGER This allows control over what percentage of the congestion window can be consumed by a single TSO frame. @@ -368,6 +317,11 @@ tcp_frto - BOOLEAN where packet loss is typically due to random radio interference rather than intermediate router congestion. +tcp_congestion_control - STRING + Set the congestion control algorithm to be used for new + connections. The algorithm "reno" is always available, but + additional choices may be available based on kernel configuration. + somaxconn - INTEGER Limit of socket listen() backlog, known in userspace as SOMAXCONN. Defaults to 128. See also tcp_max_syn_backlog for additional tuning diff --git a/Documentation/networking/tcp.txt b/Documentation/networking/tcp.txt index 71749007091e..0fa300425575 100644 --- a/Documentation/networking/tcp.txt +++ b/Documentation/networking/tcp.txt @@ -1,5 +1,72 @@ -How the new TCP output machine [nyi] works. +TCP protocol +============ +Last updated: 21 June 2005 + +Contents +======== + +- Congestion control +- How the new TCP output machine [nyi] works + +Congestion control +================== + +The following variables are used in the tcp_sock for congestion control: +snd_cwnd The size of the congestion window +snd_ssthresh Slow start threshold. We are in slow start if + snd_cwnd is less than this. +snd_cwnd_cnt A counter used to slow down the rate of increase + once we exceed slow start threshold. +snd_cwnd_clamp This is the maximum size that snd_cwnd can grow to. +snd_cwnd_stamp Timestamp for when congestion window last validated. +snd_cwnd_used Used as a highwater mark for how much of the + congestion window is in use. It is used to adjust + snd_cwnd down when the link is limited by the + application rather than the network. + +As of 2.6.13, Linux supports pluggable congestion control algorithms. +A congestion control mechanism can be registered through functions in +tcp_cong.c. The functions used by the congestion control mechanism are +registered via passing a tcp_congestion_ops struct to +tcp_register_congestion_control. As a minimum name, ssthresh, +cong_avoid, min_cwnd must be valid. + +Private data for a congestion control mechanism is stored in tp->ca_priv. +tcp_ca(tp) returns a pointer to this space. This is preallocated space - it +is important to check the size of your private data will fit this space, or +alternatively space could be allocated elsewhere and a pointer to it could +be stored here. + +There are three kinds of congestion control algorithms currently: The +simplest ones are derived from TCP reno (highspeed, scalable) and just +provide an alternative the congestion window calculation. More complex +ones like BIC try to look at other events to provide better +heuristics. There are also round trip time based algorithms like +Vegas and Westwood+. + +Good TCP congestion control is a complex problem because the algorithm +needs to maintain fairness and performance. Please review current +research and RFC's before developing new modules. + +The method that is used to determine which congestion control mechanism is +determined by the setting of the sysctl net.ipv4.tcp_congestion_control. +The default congestion control will be the last one registered (LIFO); +so if you built everything as modules. the default will be reno. If you +build with the default's from Kconfig, then BIC will be builtin (not a module) +and it will end up the default. + +If you really want a particular default value then you will need +to set it with the sysctl. If you use a sysctl, the module will be autoloaded +if needed and you will get the expected protocol. If you ask for an +unknown congestion method, then the sysctl attempt will fail. + +If you remove a tcp congestion control module, then you will get the next +available one. Since reno can not be built as a module, and can not be +deleted, it will always be available. + +How the new TCP output machine [nyi] works. +=========================================== Data is kept on a single queue. The skb->users flag tells us if the frame is one that has been queued already. To add a frame we throw it on the end. Ack diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 35159176997b..9f11d36a8c10 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -49,6 +49,7 @@ show up in /proc/sys/kernel: - shmmax [ sysv ipc ] - shmmni - stop-a [ SPARC only ] +- suid_dumpable - sysrq ==> Documentation/sysrq.txt - tainted - threads-max @@ -300,6 +301,25 @@ kernel. This value defaults to SHMMAX. ============================================================== +suid_dumpable: + +This value can be used to query and set the core dump mode for setuid +or otherwise protected/tainted binaries. The modes are + +0 - (default) - traditional behaviour. Any process which has changed + privilege levels or is execute only will not be dumped +1 - (debug) - all processes dump core when possible. The core dump is + owned by the current user and no security is applied. This is + intended for system debugging situations only. Ptrace is unchecked. +2 - (suidsafe) - any binary which normally would not be dumped is dumped + readable by root only. This allows the end user to remove + such a dump but not access it directly. For security reasons + core dumps in this mode will not overwrite one another or + other files. This mode is appropriate when adminstrators are + attempting to debug problems in a normal environment. + +============================================================== + tainted: Non-zero if the kernel has been tainted. Numeric values, which diff --git a/Documentation/tty.txt b/Documentation/tty.txt index 3958cf746dde..8ff7bc2a0811 100644 --- a/Documentation/tty.txt +++ b/Documentation/tty.txt @@ -22,7 +22,7 @@ copy of the structure. You must not re-register over the top of the line discipline even with the same data or your computer again will be eaten by demons. -In order to remove a line discipline call tty_register_ldisc passing NULL. +In order to remove a line discipline call tty_unregister_ldisc(). In ancient times this always worked. In modern times the function will return -EBUSY if the ldisc is currently in use. Since the ldisc referencing code manages the module counts this should not usually be a concern. diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index e46761c39e3f..aeeafec0594c 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -119,3 +119,17 @@ card=117 - NGS NGSTV+ card=118 - LMLBT4 card=119 - Tekram M205 PRO card=120 - Conceptronic CONTVFMi +card=121 - Euresys Picolo Tetra +card=122 - Spirit TV Tuner +card=123 - AVerMedia AVerTV DVB-T 771 +card=124 - AverMedia AverTV DVB-T 761 +card=125 - MATRIX Vision Sigma-SQ +card=126 - MATRIX Vision Sigma-SLC +card=127 - APAC Viewcomp 878(AMAX) +card=128 - DVICO FusionHDTV DVB-T Lite +card=129 - V-Gear MyVCD +card=130 - Super TV Tuner +card=131 - Tibet Systems 'Progress DVR' CS16 +card=132 - Kodicom 4400R (master) +card=133 - Kodicom 4400R (slave) +card=134 - Adlink RTV24 diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 new file mode 100644 index 000000000000..216f705495cc --- /dev/null +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -0,0 +1,29 @@ +card=0 - UNKNOWN/GENERIC +card=1 - Hauppauge WinTV 34xxx models +card=2 - GDI Black Gold +card=3 - PixelView +card=4 - ATI TV Wonder Pro +card=5 - Leadtek Winfast 2000XP Expert +card=6 - AverTV Studio 303 (M126) +card=7 - MSI TV-@nywhere Master +card=8 - Leadtek Winfast DV2000 +card=9 - Leadtek PVR 2000 +card=10 - IODATA GV-VCP3/PCI +card=11 - Prolink PlayTV PVR +card=12 - ASUS PVR-416 +card=13 - MSI TV-@nywhere +card=14 - KWorld/VStream XPert DVB-T +card=15 - DVICO FusionHDTV DVB-T1 +card=16 - KWorld LTV883RF +card=17 - DViCO - FusionHDTV 3 Gold +card=18 - Hauppauge Nova-T DVB-T +card=19 - Conexant DVB-T reference design +card=20 - Provideo PV259 +card=21 - DVICO FusionHDTV DVB-T Plus +card=22 - digitalnow DNTV Live! DVB-T +card=23 - pcHDTV HD3000 HDTV +card=24 - Hauppauge WinTV 28xxx (Roslyn) models +card=25 - Digital-Logic MICROSPACE Entertainment Center (MEC) +card=26 - IODATA GV/BCTV7E +card=27 - PixelView PlayTV Ultra Pro (Stereo) +card=28 - DViCO - FusionHDTV 3 Gold-T diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index a6c82fa4de02..d5ed95d28500 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -20,16 +20,37 @@ 19 -> Compro VideoMate TV [185b:c100] 20 -> Matrox CronosPlus [102B:48d0] 21 -> 10MOONS PCI TV CAPTURE CARD [1131:2001] - 22 -> Medion 2819/ AverMedia M156 [1461:a70b,1461:2115] + 22 -> AverMedia M156 / Medion 2819 [1461:a70b] 23 -> BMK MPEX Tuner 24 -> KNC One TV-Station DVR [1894:a006] 25 -> ASUS TV-FM 7133 [1043:4843] 26 -> Pinnacle PCTV Stereo (saa7134) [11bd:002b] - 27 -> Manli MuchTV M-TV002 - 28 -> Manli MuchTV M-TV001 + 27 -> Manli MuchTV M-TV002/Behold TV 403 FM + 28 -> Manli MuchTV M-TV001/Behold TV 401 29 -> Nagase Sangyo TransGear 3000TV [1461:050c] 30 -> Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) [1019:4cb4] 31 -> Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM) [1019:4cb5] 32 -> AVACS SmartTV 33 -> AVerMedia DVD EZMaker [1461:10ff] - 34 -> LifeView FlyTV Platinum33 mini [5168:0212] + 34 -> Noval Prime TV 7133 + 35 -> AverMedia AverTV Studio 305 [1461:2115] + 37 -> Items MuchTV Plus / IT-005 + 38 -> Terratec Cinergy 200 TV [153B:1152] + 39 -> LifeView FlyTV Platinum Mini [5168:0212] + 40 -> Compro VideoMate TV PVR/FM [185b:c100] + 41 -> Compro VideoMate TV Gold+ [185b:c100] + 42 -> Sabrent SBT-TVFM (saa7130) + 43 -> :Zolid Xpert TV7134 + 44 -> Empire PCI TV-Radio LE + 45 -> Avermedia AVerTV Studio 307 [1461:9715] + 46 -> AVerMedia Cardbus TV/Radio [1461:d6ee] + 47 -> Terratec Cinergy 400 mobile [153b:1162] + 48 -> Terratec Cinergy 600 TV MK3 [153B:1158] + 49 -> Compro VideoMate Gold+ Pal [185b:c200] + 50 -> Pinnacle PCTV 300i DVB-T + PAL [11bd:002d] + 51 -> ProVideo PV952 [1540:9524] + 52 -> AverMedia AverTV/305 [1461:2108] + 54 -> LifeView FlyTV Platinum FM [5168:0214,1489:0214] + 55 -> LifeView FlyDVB-T DUO [5168:0306] + 56 -> Avermedia AVerTV 307 [1461:a70a] + 57 -> Avermedia AVerTV GO 007 FM [1461:f31f] diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner index f7bafe862ba0..aeb8df8ce890 100644 --- a/Documentation/video4linux/CARDLIST.tuner +++ b/Documentation/video4linux/CARDLIST.tuner @@ -44,3 +44,18 @@ tuner=42 - Philips 1236D ATSC/NTSC daul in tuner=43 - Philips NTSC MK3 (FM1236MK3 or FM1236/F) tuner=44 - Philips 4 in 1 (ATI TV Wonder Pro/Conexant) tuner=45 - Microtune 4049 FM5 +tuner=46 - Panasonic VP27s/ENGE4324D +tuner=47 - LG NTSC (TAPE series) +tuner=48 - Tenna TNF 8831 BGFF) +tuner=49 - Microtune 4042 FI5 ATSC/NTSC dual in +tuner=50 - TCL 2002N +tuner=51 - Philips PAL/SECAM_D (FM 1256 I-H3) +tuner=52 - Thomson DDT 7610 (ATSC/NTSC) +tuner=53 - Philips FQ1286 +tuner=54 - tda8290+75 +tuner=55 - LG PAL (TAPE series) +tuner=56 - Philips PAL/SECAM multi (FQ1216AME MK4) +tuner=57 - Philips FQ1236A MK4 +tuner=58 - Ymec TVision TVF-8531MF +tuner=59 - Ymec TVision TVF-5533MF +tuner=60 - Thomson DDT 7611 (ATSC/NTSC) diff --git a/Documentation/video4linux/hauppauge-wintv-cx88-ir.txt b/Documentation/video4linux/hauppauge-wintv-cx88-ir.txt new file mode 100644 index 000000000000..93fec32a1188 --- /dev/null +++ b/Documentation/video4linux/hauppauge-wintv-cx88-ir.txt @@ -0,0 +1,54 @@ +The controls for the mux are GPIO [0,1] for source, and GPIO 2 for muting. + +GPIO0 GPIO1 + 0 0 TV Audio + 1 0 FM radio + 0 1 Line-In + 1 1 Mono tuner bypass or CD passthru (tuner specific) + +GPIO 16(i believe) is tied to the IR port (if present). + +------------------------------------------------------------------------------------ + +>From the data sheet: + Register 24'h20004 PCI Interrupt Status + bit [18] IR_SMP_INT Set when 32 input samples have been collected over + gpio[16] pin into GP_SAMPLE register. + +What's missing from the data sheet: + +Setup 4KHz sampling rate (roughly 2x oversampled; good enough for our RC5 +compat remote) +set register 0x35C050 to 0xa80a80 + +enable sampling +set register 0x35C054 to 0x5 + +Of course, enable the IRQ bit 18 in the interrupt mask register .(and +provide for a handler) + +GP_SAMPLE register is at 0x35C058 + +Bits are then right shifted into the GP_SAMPLE register at the specified +rate; you get an interrupt when a full DWORD is recieved. +You need to recover the actual RC5 bits out of the (oversampled) IR sensor +bits. (Hint: look for the 0/1and 1/0 crossings of the RC5 bi-phase data) An +actual raw RC5 code will span 2-3 DWORDS, depending on the actual alignment. + +I'm pretty sure when no IR signal is present the receiver is always in a +marking state(1); but stray light, etc can cause intermittent noise values +as well. Remember, this is a free running sample of the IR receiver state +over time, so don't assume any sample starts at any particular place. + +http://www.atmel.com/dyn/resources/prod_documents/doc2817.pdf +This data sheet (google search) seems to have a lovely description of the +RC5 basics + +http://users.pandora.be/nenya/electronics/rc5/ and more data + +http://www.ee.washington.edu/circuit_archive/text/ir_decode.txt +and even a reference to how to decode a bi-phase data stream. + +http://www.xs4all.nl/~sbp/knowledge/ir/rc5.htm +still more info + diff --git a/Documentation/video4linux/lifeview.txt b/Documentation/video4linux/lifeview.txt new file mode 100644 index 000000000000..b07ea79c2b7e --- /dev/null +++ b/Documentation/video4linux/lifeview.txt @@ -0,0 +1,42 @@ +collecting data about the lifeview models and the config coding on +gpio pins 0-9 ... +================================================================== + +bt878: + LR50 rev. Q ("PARTS: 7031505116), Tuner wurde als Nr. 5 erkannt, Eingänge + SVideo, TV, Composite, Audio, Remote. CP9..1=100001001 (1: 0-Ohm-Widerstand + gegen GND unbestückt; 0: bestückt) + +------------------------------------------------------------------------------ + +saa7134: + /* LifeView FlyTV Platinum FM (LR214WF) */ + /* "Peter Missel */ + .name = "LifeView FlyTV Platinum FM", + /* GP27 MDT2005 PB4 pin 10 */ + /* GP26 MDT2005 PB3 pin 9 */ + /* GP25 MDT2005 PB2 pin 8 */ + /* GP23 MDT2005 PB1 pin 7 */ + /* GP22 MDT2005 PB0 pin 6 */ + /* GP21 MDT2005 PB5 pin 11 */ + /* GP20 MDT2005 PB6 pin 12 */ + /* GP19 MDT2005 PB7 pin 13 */ + /* nc MDT2005 PA3 pin 2 */ + /* Remote MDT2005 PA2 pin 1 */ + /* GP18 MDT2005 PA1 pin 18 */ + /* nc MDT2005 PA0 pin 17 strap low */ + + /* GP17 Strap "GP7"=High */ + /* GP16 Strap "GP6"=High + 0=Radio 1=TV + Drives SA630D ENCH1 and HEF4052 A1 pins + to do FM radio through SIF input */ + /* GP15 nc */ + /* GP14 nc */ + /* GP13 nc */ + /* GP12 Strap "GP5" = High */ + /* GP11 Strap "GP4" = High */ + /* GP10 Strap "GP3" = High */ + /* GP09 Strap "GP2" = Low */ + /* GP08 Strap "GP1" = Low */ + /* GP07.00 nc */ diff --git a/Documentation/video4linux/not-in-cx2388x-datasheet.txt b/Documentation/video4linux/not-in-cx2388x-datasheet.txt new file mode 100644 index 000000000000..96b638b5ba1d --- /dev/null +++ b/Documentation/video4linux/not-in-cx2388x-datasheet.txt @@ -0,0 +1,37 @@ +================================================================================= +MO_OUTPUT_FORMAT (0x310164) + + Previous default from DScaler: 0x1c1f0008 + Digit 8: 31-28 + 28: PREVREMOD = 1 + + Digit 7: 27-24 (0xc = 12 = b1100 ) + 27: COMBALT = 1 + 26: PAL_INV_PHASE + (DScaler apparently set this to 1, resulted in sucky picture) + + Digits 6,5: 23-16 + 25-16: COMB_RANGE = 0x1f [default] (9 bits -> max 512) + + Digit 4: 15-12 + 15: DISIFX = 0 + 14: INVCBF = 0 + 13: DISADAPT = 0 + 12: NARROWADAPT = 0 + + Digit 3: 11-8 + 11: FORCE2H + 10: FORCEREMD + 9: NCHROMAEN + 8: NREMODEN + + Digit 2: 7-4 + 7-6: YCORE + 5-4: CCORE + + Digit 1: 3-0 + 3: RANGE = 1 + 2: HACTEXT + 1: HSFMT + +================================================================================= diff --git a/MAINTAINERS b/MAINTAINERS index 4d44824884ef..a07eeb411370 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -304,7 +304,7 @@ S: Maintained ARM/PT DIGITAL BOARD PORT P: Stefan Eletzhofer M: stefan.eletzhofer@eletztrick.de -L: linux-arm-kernel@lists.arm.linux.org.uk +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) W: http://www.arm.linux.org.uk/ S: Maintained @@ -317,21 +317,21 @@ S: Maintained ARM/STRONGARM110 PORT P: Russell King M: rmk@arm.linux.org.uk -L: linux-arm-kernel@lists.arm.linux.org.uk +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) W: http://www.arm.linux.org.uk/ S: Maintained ARM/S3C2410 ARM ARCHITECTURE P: Ben Dooks M: ben-s3c2410@fluff.org -L: linux-arm-kernel@lists.arm.linux.org.uk +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) W: http://www.fluff.org/ben/linux/ S: Maintained ARM/S3C2440 ARM ARCHITECTURE P: Ben Dooks M: ben-s3c2440@fluff.org -L: linux-arm-kernel@lists.arm.linux.org.uk +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) W: http://www.fluff.org/ben/linux/ S: Maintained @@ -504,6 +504,13 @@ L: bonding-devel@lists.sourceforge.net W: http://sourceforge.net/projects/bonding/ S: Supported +BROADBAND PROCESSOR ARCHITECTURE +P: Arnd Bergmann +M: arnd@arndb.de +L: linuxppc64-dev@ozlabs.org +W: http://linuxppc64.org +S: Supported + BTTV VIDEO4LINUX DRIVER P: Gerd Knorr M: kraxel@bytesex.org @@ -1853,7 +1860,7 @@ S: Maintained PXA2xx SUPPORT P: Nicolas Pitre M: nico@cam.org -L: linux-arm-kernel@lists.arm.linux.org.uk +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) S: Maintained QLOGIC QLA2XXX FC-SCSI DRIVER @@ -2138,6 +2145,11 @@ W: http://tpmdd.sourceforge.net L: tpmdd-devel@lists.sourceforge.net S: Maintained +TENSILICA XTENSA PORT (xtensa): +P: Chris Zankel +M: chris@zankel.net +S: Maintained + UltraSPARC (sparc64): P: David S. Miller M: davem@davemloft.net @@ -2155,7 +2167,7 @@ SHARP LH SUPPORT (LH7952X & LH7A40X) P: Marc Singer M: elf@buici.com W: http://projects.buici.com/arm -L: linux-arm-kernel@lists.arm.linux.org.uk +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) S: Maintained SPARC (sparc32): diff --git a/Makefile b/Makefile index 0d1e74d50067..fad349724e99 100644 --- a/Makefile +++ b/Makefile @@ -518,7 +518,7 @@ CFLAGS += $(call add-align,CONFIG_CC_ALIGN_LOOPS,-loops) CFLAGS += $(call add-align,CONFIG_CC_ALIGN_JUMPS,-jumps) ifdef CONFIG_FRAME_POINTER -CFLAGS += -fno-omit-frame-pointer +CFLAGS += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,) else CFLAGS += -fomit-frame-pointer endif diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index f7c96635d3b4..c5739d6309df 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -509,7 +509,7 @@ config NR_CPUS depends on SMP default "64" -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool "Discontiguous Memory Support (EXPERIMENTAL)" depends on EXPERIMENTAL help @@ -518,6 +518,8 @@ config DISCONTIGMEM or have huge holes in the physical address space for other reasons. See for more. +source "mm/Kconfig" + config NUMA bool "NUMA Support (EXPERIMENTAL)" depends on DISCONTIGMEM diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index 5e39b7a7c8f4..6da9c3dbde44 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -96,7 +96,7 @@ CONFIG_ALPHA_CORE_AGP=y CONFIG_ALPHA_BROKEN_IRQ_MASK=y CONFIG_EISA=y # CONFIG_SMP is not set -# CONFIG_DISCONTIGMEM is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_VERBOSE_MCHECK=y CONFIG_VERBOSE_MCHECK_ON=1 CONFIG_PCI_LEGACY_PROC=y diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index ba81c4422aaf..c7481d59b6df 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -327,8 +327,6 @@ void __init mem_init(void) extern char _text, _etext, _data, _edata; extern char __init_begin, __init_end; unsigned long nid, i; - struct page * lmem_map; - high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); reservedpages = 0; @@ -338,10 +336,10 @@ void __init mem_init(void) */ totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); - lmem_map = node_mem_map(nid); pfn = NODE_DATA(nid)->node_start_pfn; for (i = 0; i < node_spanned_pages(nid); i++, pfn++) - if (page_is_ram(pfn) && PageReserved(lmem_map+i)) + if (page_is_ram(pfn) && + PageReserved(nid_page_nr(nid, i))) reservedpages++; } @@ -373,18 +371,18 @@ show_mem(void) show_free_areas(); printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); for_each_online_node(nid) { - struct page * lmem_map = node_mem_map(nid); i = node_spanned_pages(nid); while (i-- > 0) { + struct page *page = nid_page_nr(nid, i); total++; - if (PageReserved(lmem_map+i)) + if (PageReserved(page)) reserved++; - else if (PageSwapCache(lmem_map+i)) + else if (PageSwapCache(page)) cached++; - else if (!page_count(lmem_map+i)) + else if (!page_count(page)) free++; else - shared += page_count(lmem_map + i) - 1; + shared += page_count(page) - 1; } } printk("%ld pages of RAM\n",total); diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ee8a9ad7bbd9..07ba77c19f6c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -346,7 +346,7 @@ config PREEMPT Say Y here if you are building a kernel for a desktop, embedded or real-time system. Say N if you are unsure. -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool default (ARCH_LH7A40X && !LH7A40X_CONTIGMEM) help @@ -355,6 +355,8 @@ config DISCONTIGMEM or have huge holes in the physical address space for other reasons. See for more. +source "mm/Kconfig" + config LEDS bool "Timer and CPU usage LEDs" depends on ARCH_CDB89712 || ARCH_CO285 || ARCH_EBSA110 || \ diff --git a/arch/arm/boot/install.sh b/arch/arm/boot/install.sh index 935bb27369e9..9f9bed207345 100644 --- a/arch/arm/boot/install.sh +++ b/arch/arm/boot/install.sh @@ -21,8 +21,8 @@ # # 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 +if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi +if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then exec /sbin/${CROSS_COMPILE}installkernel "$@"; fi if [ "$(basename $2)" = "zImage" ]; then # Compressed install diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig index 2a63fb277196..98b72ff38832 100644 --- a/arch/arm/configs/s3c2410_defconfig +++ b/arch/arm/configs/s3c2410_defconfig @@ -1,14 +1,13 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-rc1-bk2 -# Sun Mar 27 17:47:45 2005 +# Linux kernel version: 2.6.12-git4 +# Wed Jun 22 15:56:42 2005 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_IOMAP=y # # Code maturity level options @@ -17,6 +16,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_CLEAN_COMPILE is not set CONFIG_BROKEN=y CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 # # General setup @@ -35,6 +35,8 @@ CONFIG_KOBJECT_UEVENT=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y @@ -81,6 +83,7 @@ CONFIG_ARCH_S3C2410=y # CONFIG_ARCH_VERSATILE is not set # CONFIG_ARCH_IMX is not set # CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set # # S3C24XX Implementations @@ -134,6 +137,7 @@ CONFIG_CPU_TLB_V4WBI=y # # Bus support # +CONFIG_ISA_DMA_API=y # # PCCARD (PCMCIA/CardBus) support @@ -143,7 +147,9 @@ CONFIG_CPU_TLB_V4WBI=y # # Kernel Features # +# CONFIG_SMP is not set # CONFIG_PREEMPT is not set +# CONFIG_DISCONTIGMEM is not set CONFIG_ALIGNMENT_TRAP=y # @@ -297,7 +303,6 @@ CONFIG_PARPORT_1284=y # # Block devices # -# CONFIG_BLK_DEV_FD is not set # CONFIG_PARIDE is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y @@ -359,6 +364,7 @@ CONFIG_BLK_DEV_IDE_BAST=y # # Fusion MPT device support # +# CONFIG_FUSION is not set # # IEEE 1394 (FireWire) support @@ -378,10 +384,11 @@ CONFIG_NET=y # Networking options # # CONFIG_PACKET is not set -# CONFIG_NETLINK_DEV is not set CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_PNP=y @@ -443,8 +450,9 @@ CONFIG_NETDEVICES=y # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_MII is not set +CONFIG_MII=m # CONFIG_SMC91X is not set +CONFIG_DM9000=m # # Ethernet (1000 Mbit) @@ -521,7 +529,6 @@ CONFIG_SERIO_SERPORT=y CONFIG_SERIO_LIBPS2=y # CONFIG_SERIO_RAW is not set # CONFIG_GAMEPORT is not set -CONFIG_SOUND_GAMEPORT=y # # Character devices @@ -605,7 +612,6 @@ CONFIG_S3C2410_RTC=y # # TPM devices # -# CONFIG_TCG_TPM is not set # # I2C support @@ -654,6 +660,7 @@ CONFIG_SENSORS_LM78=m CONFIG_SENSORS_LM85=m # CONFIG_SENSORS_LM87 is not set # CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set # CONFIG_SENSORS_MAX1619 is not set # CONFIG_SENSORS_PC87360 is not set # CONFIG_SENSORS_SMSC47B397 is not set @@ -665,6 +672,7 @@ CONFIG_SENSORS_LM85=m # # Other I2C Chip support # +# CONFIG_SENSORS_DS1337 is not set CONFIG_SENSORS_EEPROM=m # CONFIG_SENSORS_PCF8574 is not set # CONFIG_SENSORS_PCF8591 is not set @@ -696,8 +704,10 @@ CONFIG_FB=y # CONFIG_FB_CFB_COPYAREA is not set # CONFIG_FB_CFB_IMAGEBLIT is not set # CONFIG_FB_SOFT_CURSOR is not set +# CONFIG_FB_MACMODES is not set CONFIG_FB_MODE_HELPERS=y # CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set # CONFIG_FB_VIRTUAL is not set # @@ -782,7 +792,6 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_PROC_FS=y CONFIG_SYSFS=y -# CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set # CONFIG_TMPFS is not set # CONFIG_HUGETLBFS is not set diff --git a/arch/arm/mach-ixp2000/core.c b/arch/arm/mach-ixp2000/core.c index fc0555596d6d..0ee34acb8d7b 100644 --- a/arch/arm/mach-ixp2000/core.c +++ b/arch/arm/mach-ixp2000/core.c @@ -40,6 +40,8 @@ #include #include +#include + static DEFINE_SPINLOCK(ixp2000_slowport_lock); static unsigned long ixp2000_slowport_irq_flags; @@ -179,7 +181,7 @@ static int ixp2000_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* clear timer 1 */ ixp2000_reg_write(IXP2000_T1_CLR, 1); - + while ((next_jiffy_time - *missing_jiffy_timer_csr) > ticks_per_jiffy) { timer_tick(regs); next_jiffy_time -= ticks_per_jiffy; @@ -238,35 +240,40 @@ void __init ixp2000_init_time(unsigned long tick_rate) /************************************************************************* * GPIO helpers *************************************************************************/ -static unsigned long GPIO_IRQ_rising_edge; static unsigned long GPIO_IRQ_falling_edge; +static unsigned long GPIO_IRQ_rising_edge; static unsigned long GPIO_IRQ_level_low; static unsigned long GPIO_IRQ_level_high; -void gpio_line_config(int line, int style) +static void update_gpio_int_csrs(void) +{ + ixp2000_reg_write(IXP2000_GPIO_FEDR, GPIO_IRQ_falling_edge); + ixp2000_reg_write(IXP2000_GPIO_REDR, GPIO_IRQ_rising_edge); + ixp2000_reg_write(IXP2000_GPIO_LSLR, GPIO_IRQ_level_low); + ixp2000_reg_write(IXP2000_GPIO_LSHR, GPIO_IRQ_level_high); +} + +void gpio_line_config(int line, int direction) { unsigned long flags; local_irq_save(flags); + if (direction == GPIO_OUT) { + irq_desc[line + IRQ_IXP2000_GPIO0].valid = 0; - if(style == GPIO_OUT) { /* if it's an output, it ain't an interrupt anymore */ - ixp2000_reg_write(IXP2000_GPIO_PDSR, (1 << line)); GPIO_IRQ_falling_edge &= ~(1 << line); GPIO_IRQ_rising_edge &= ~(1 << line); GPIO_IRQ_level_low &= ~(1 << line); GPIO_IRQ_level_high &= ~(1 << line); - ixp2000_reg_write(IXP2000_GPIO_FEDR, GPIO_IRQ_falling_edge); - ixp2000_reg_write(IXP2000_GPIO_REDR, GPIO_IRQ_rising_edge); - ixp2000_reg_write(IXP2000_GPIO_LSHR, GPIO_IRQ_level_high); - ixp2000_reg_write(IXP2000_GPIO_LSLR, GPIO_IRQ_level_low); - irq_desc[line+IRQ_IXP2000_GPIO0].valid = 0; - } else if(style == GPIO_IN) { - ixp2000_reg_write(IXP2000_GPIO_PDCR, (1 << line)); + update_gpio_int_csrs(); + + ixp2000_reg_write(IXP2000_GPIO_PDSR, 1 << line); + } else if (direction == GPIO_IN) { + ixp2000_reg_write(IXP2000_GPIO_PDCR, 1 << line); } - local_irq_restore(flags); -} +} /************************************************************************* @@ -285,9 +292,50 @@ static void ixp2000_GPIO_irq_handler(unsigned int irq, struct irqdesc *desc, str } } +static int ixp2000_GPIO_irq_type(unsigned int irq, unsigned int type) +{ + int line = irq - IRQ_IXP2000_GPIO0; + + /* + * First, configure this GPIO line as an input. + */ + ixp2000_reg_write(IXP2000_GPIO_PDCR, 1 << line); + + /* + * Then, set the proper trigger type. + */ + if (type & IRQT_FALLING) + GPIO_IRQ_falling_edge |= 1 << line; + else + GPIO_IRQ_falling_edge &= ~(1 << line); + if (type & IRQT_RISING) + GPIO_IRQ_rising_edge |= 1 << line; + else + GPIO_IRQ_rising_edge &= ~(1 << line); + if (type & IRQT_LOW) + GPIO_IRQ_level_low |= 1 << line; + else + GPIO_IRQ_level_low &= ~(1 << line); + if (type & IRQT_HIGH) + GPIO_IRQ_level_high |= 1 << line; + else + GPIO_IRQ_level_high &= ~(1 << line); + update_gpio_int_csrs(); + + /* + * Finally, mark the corresponding IRQ as valid. + */ + irq_desc[irq].valid = 1; + + return 0; +} + static void ixp2000_GPIO_irq_mask_ack(unsigned int irq) { ixp2000_reg_write(IXP2000_GPIO_INCR, (1 << (irq - IRQ_IXP2000_GPIO0))); + + ixp2000_reg_write(IXP2000_GPIO_EDSR, (1 << (irq - IRQ_IXP2000_GPIO0))); + ixp2000_reg_write(IXP2000_GPIO_LDSR, (1 << (irq - IRQ_IXP2000_GPIO0))); ixp2000_reg_write(IXP2000_GPIO_INST, (1 << (irq - IRQ_IXP2000_GPIO0))); } @@ -302,6 +350,7 @@ static void ixp2000_GPIO_irq_unmask(unsigned int irq) } static struct irqchip ixp2000_GPIO_irq_chip = { + .type = ixp2000_GPIO_irq_type, .ack = ixp2000_GPIO_irq_mask_ack, .mask = ixp2000_GPIO_irq_mask, .unmask = ixp2000_GPIO_irq_unmask @@ -338,7 +387,7 @@ static void ixp2000_irq_mask(unsigned int irq) static void ixp2000_irq_unmask(unsigned int irq) { - ixp2000_reg_write(IXP2000_IRQ_ENABLE_SET, (1 << irq)); + ixp2000_reg_write(IXP2000_IRQ_ENABLE_SET, (1 << irq)); } static struct irqchip ixp2000_irq_chip = { @@ -375,16 +424,16 @@ void __init ixp2000_init_irq(void) * our mask/unmask code much simpler. */ for (irq = IRQ_IXP2000_SOFT_INT; irq <= IRQ_IXP2000_THDB3; irq++) { - if((1 << irq) & IXP2000_VALID_IRQ_MASK) { + if ((1 << irq) & IXP2000_VALID_IRQ_MASK) { set_irq_chip(irq, &ixp2000_irq_chip); set_irq_handler(irq, do_level_IRQ); set_irq_flags(irq, IRQF_VALID); } else set_irq_flags(irq, 0); } - + /* * GPIO IRQs are invalid until someone sets the interrupt mode - * by calling gpio_line_set(); + * by calling set_irq_type(). */ for (irq = IRQ_IXP2000_GPIO0; irq <= IRQ_IXP2000_GPIO7; irq++) { set_irq_chip(irq, &ixp2000_GPIO_irq_chip); diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 267ba02d77dc..f39e8408488f 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -141,7 +141,15 @@ static struct map_desc ixp4xx_io_desc[] __initdata = { .physical = IXP4XX_PCI_CFG_BASE_PHYS, .length = IXP4XX_PCI_CFG_REGION_SIZE, .type = MT_DEVICE + }, +#ifdef CONFIG_DEBUG_LL + { /* Debug UART mapping */ + .virtual = IXP4XX_DEBUG_UART_BASE_VIRT, + .physical = IXP4XX_DEBUG_UART_BASE_PHYS, + .length = IXP4XX_DEBUG_UART_REGION_SIZE, + .type = MT_DEVICE } +#endif }; void __init ixp4xx_map_io(void) diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c index 3bb97eb6e693..f3e970039b65 100644 --- a/arch/arm/mach-s3c2410/mach-bast.c +++ b/arch/arm/mach-s3c2410/mach-bast.c @@ -26,6 +26,7 @@ * 03-Mar-2005 BJD Ensured that bast-cpld.h is included * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA * 14-Mar-2006 BJD Updated for __iomem changes + * 22-Jun-2006 BJD Added DM9000 platform information */ #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include @@ -53,6 +55,7 @@ #include #include #include +#include #include #include @@ -112,7 +115,6 @@ static struct map_desc bast_iodesc[] __initdata = { { VA_C2(BAST_VA_ISAMEM), PA_CS2(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, { VA_C2(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, { VA_C2(BAST_VA_SUPERIO), PA_CS2(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, - { VA_C2(BAST_VA_DM9000), PA_CS2(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C2(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C2(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C2(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, @@ -123,7 +125,6 @@ static struct map_desc bast_iodesc[] __initdata = { { VA_C3(BAST_VA_ISAMEM), PA_CS3(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, { VA_C3(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, { VA_C3(BAST_VA_SUPERIO), PA_CS3(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, - { VA_C3(BAST_VA_DM9000), PA_CS3(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C3(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C3(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C3(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, @@ -134,7 +135,6 @@ static struct map_desc bast_iodesc[] __initdata = { { VA_C4(BAST_VA_ISAMEM), PA_CS4(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, { VA_C4(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, { VA_C4(BAST_VA_SUPERIO), PA_CS4(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, - { VA_C4(BAST_VA_DM9000), PA_CS4(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C4(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C4(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C4(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, @@ -145,7 +145,6 @@ static struct map_desc bast_iodesc[] __initdata = { { VA_C5(BAST_VA_ISAMEM), PA_CS5(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, { VA_C5(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, { VA_C5(BAST_VA_SUPERIO), PA_CS5(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, - { VA_C5(BAST_VA_DM9000), PA_CS5(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C5(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C5(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C5(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, @@ -313,6 +312,45 @@ static struct s3c2410_platform_nand bast_nand_info = { .select_chip = bast_nand_select, }; +/* DM9000 */ + +static struct resource bast_dm9k_resource[] = { + [0] = { + .start = S3C2410_CS5 + BAST_PA_DM9000, + .end = S3C2410_CS5 + BAST_PA_DM9000 + 3, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = S3C2410_CS5 + BAST_PA_DM9000 + 0x40, + .end = S3C2410_CS5 + BAST_PA_DM9000 + 0x40 + 0x3f, + .flags = IORESOURCE_MEM + }, + [2] = { + .start = IRQ_DM9000, + .end = IRQ_DM9000, + .flags = IORESOURCE_IRQ + } + +}; + +/* for the moment we limit ourselves to 16bit IO until some + * better IO routines can be written and tested +*/ + +struct dm9000_plat_data bast_dm9k_platdata = { + .flags = DM9000_PLATF_16BITONLY +}; + +static struct platform_device bast_device_dm9k = { + .name = "dm9000", + .id = 0, + .num_resources = ARRAY_SIZE(bast_dm9k_resource), + .resource = bast_dm9k_resource, + .dev = { + .platform_data = &bast_dm9k_platdata, + } +}; + /* Standard BAST devices */ @@ -324,7 +362,8 @@ static struct platform_device *bast_devices[] __initdata = { &s3c_device_iis, &s3c_device_rtc, &s3c_device_nand, - &bast_device_nor + &bast_device_nor, + &bast_device_dm9k, }; static struct clk *bast_clocks[] = { diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c index 5512146b1ce4..76be074944a0 100644 --- a/arch/arm/mach-s3c2410/mach-vr1000.c +++ b/arch/arm/mach-s3c2410/mach-vr1000.c @@ -27,6 +27,7 @@ * 10-Feb-2005 BJD Added power-off capability * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA * 14-Mar-2006 BJD void __iomem fixes + * 22-Jun-2006 BJD Added DM9000 platform information */ #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include @@ -98,28 +100,24 @@ static struct map_desc vr1000_iodesc[] __initdata = { * are only 8bit */ /* slow, byte */ - { VA_C2(VR1000_VA_DM9000), PA_CS2(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C2(VR1000_VA_IDEPRI), PA_CS3(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C2(VR1000_VA_IDESEC), PA_CS3(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C2(VR1000_VA_IDEPRIAUX), PA_CS3(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, { VA_C2(VR1000_VA_IDESECAUX), PA_CS3(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, /* slow, word */ - { VA_C3(VR1000_VA_DM9000), PA_CS3(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C3(VR1000_VA_IDEPRI), PA_CS3(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C3(VR1000_VA_IDESEC), PA_CS3(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C3(VR1000_VA_IDEPRIAUX), PA_CS3(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, { VA_C3(VR1000_VA_IDESECAUX), PA_CS3(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, /* fast, byte */ - { VA_C4(VR1000_VA_DM9000), PA_CS4(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C4(VR1000_VA_IDEPRI), PA_CS5(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C4(VR1000_VA_IDESEC), PA_CS5(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C4(VR1000_VA_IDEPRIAUX), PA_CS5(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, { VA_C4(VR1000_VA_IDESECAUX), PA_CS5(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, /* fast, word */ - { VA_C5(VR1000_VA_DM9000), PA_CS5(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, { VA_C5(VR1000_VA_IDEPRI), PA_CS5(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, { VA_C5(VR1000_VA_IDESEC), PA_CS5(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, { VA_C5(VR1000_VA_IDEPRIAUX), PA_CS5(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, @@ -246,6 +244,74 @@ static struct platform_device vr1000_nor = { .resource = vr1000_nor_resource, }; +/* DM9000 ethernet devices */ + +static struct resource vr1000_dm9k0_resource[] = { + [0] = { + .start = S3C2410_CS5 + VR1000_PA_DM9000, + .end = S3C2410_CS5 + VR1000_PA_DM9000 + 3, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = S3C2410_CS5 + VR1000_PA_DM9000 + 0x40, + .end = S3C2410_CS5 + VR1000_PA_DM9000 + 0x7f, + .flags = IORESOURCE_MEM + }, + [2] = { + .start = IRQ_VR1000_DM9000A, + .end = IRQ_VR1000_DM9000A, + .flags = IORESOURCE_IRQ + } + +}; + +static struct resource vr1000_dm9k1_resource[] = { + [0] = { + .start = S3C2410_CS5 + VR1000_PA_DM9000 + 0x80, + .end = S3C2410_CS5 + VR1000_PA_DM9000 + 0x83, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = S3C2410_CS5 + VR1000_PA_DM9000 + 0xC0, + .end = S3C2410_CS5 + VR1000_PA_DM9000 + 0xFF, + .flags = IORESOURCE_MEM + }, + [2] = { + .start = IRQ_VR1000_DM9000N, + .end = IRQ_VR1000_DM9000N, + .flags = IORESOURCE_IRQ + } +}; + +/* for the moment we limit ourselves to 16bit IO until some + * better IO routines can be written and tested +*/ + +struct dm9000_plat_data vr1000_dm9k_platdata = { + .flags = DM9000_PLATF_16BITONLY, +}; + +static struct platform_device vr1000_dm9k0 = { + .name = "dm9000", + .id = 0, + .num_resources = ARRAY_SIZE(vr1000_dm9k0_resource), + .resource = vr1000_dm9k0_resource, + .dev = { + .platform_data = &vr1000_dm9k_platdata, + } +}; + +static struct platform_device vr1000_dm9k1 = { + .name = "dm9000", + .id = 1, + .num_resources = ARRAY_SIZE(vr1000_dm9k1_resource), + .resource = vr1000_dm9k1_resource, + .dev = { + .platform_data = &vr1000_dm9k_platdata, + } +}; + +/* devices for this board */ static struct platform_device *vr1000_devices[] __initdata = { &s3c_device_usb, @@ -253,8 +319,11 @@ static struct platform_device *vr1000_devices[] __initdata = { &s3c_device_wdt, &s3c_device_i2c, &s3c_device_iis, + &s3c_device_adc, &serial_device, &vr1000_nor, + &vr1000_dm9k0, + &vr1000_dm9k1 }; static struct clk *vr1000_clocks[] = { diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 0aa73d414783..e3d8510f4340 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -132,8 +132,8 @@ ENTRY(cpu_v6_switch_mm) * 100x 1 0 1 r/o no acc * 10x0 1 0 1 r/o no acc * 1011 0 0 1 r/w no acc - * 110x 1 1 0 r/o r/o - * 11x0 1 1 0 r/o r/o + * 110x 0 1 0 r/w r/o + * 11x0 0 1 0 r/w r/o * 1111 0 1 1 r/w r/w */ ENTRY(cpu_v6_set_pte) @@ -150,7 +150,7 @@ ENTRY(cpu_v6_set_pte) tst r1, #L_PTE_USER orrne r2, r2, #AP1 | nG tstne r2, #APX - eorne r2, r2, #AP0 + bicne r2, r2, #APX | AP0 tst r1, #L_PTE_YOUNG biceq r2, r2, #APX | AP1 | AP0 diff --git a/arch/arm/nwfpe/softfloat-macros b/arch/arm/nwfpe/softfloat-macros index 5469989f2c5e..5a060f95a58f 100644 --- a/arch/arm/nwfpe/softfloat-macros +++ b/arch/arm/nwfpe/softfloat-macros @@ -563,8 +563,14 @@ static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) bits64 rem0, rem1, term0, term1; bits64 z; if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF ); - b0 = b>>32; - z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32; + b0 = b>>32; /* hence b0 is 32 bits wide now */ + if ( b0<<32 <= a0 ) { + z = LIT64( 0xFFFFFFFF00000000 ); + } else { + z = a0; + do_div( z, b0 ); + z <<= 32; + } mul64To128( b, z, &term0, &term1 ); sub128( a0, a1, term0, term1, &rem0, &rem1 ); while ( ( (sbits64) rem0 ) < 0 ) { @@ -573,7 +579,12 @@ static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) add128( rem0, rem1, b0, b1, &rem0, &rem1 ); } rem0 = ( rem0<<32 ) | ( rem1>>32 ); - z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0; + if ( b0<<32 <= rem0 ) { + z |= 0xFFFFFFFF; + } else { + do_div( rem0, b0 ); + z |= rem0; + } return z; } @@ -601,6 +612,7 @@ static bits32 estimateSqrt32( int16 aExp, bits32 a ) }; int8 index; bits32 z; + bits64 A; index = ( a>>27 ) & 15; if ( aExp & 1 ) { @@ -614,7 +626,9 @@ static bits32 estimateSqrt32( int16 aExp, bits32 a ) z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 ); if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 ); } - return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 ); + A = ( (bits64) a )<<31; + do_div( A, z ); + return ( (bits32) A ) + ( z>>1 ); } diff --git a/arch/arm/nwfpe/softfloat.c b/arch/arm/nwfpe/softfloat.c index 9d743ae29062..e038dd3be9b3 100644 --- a/arch/arm/nwfpe/softfloat.c +++ b/arch/arm/nwfpe/softfloat.c @@ -28,6 +28,8 @@ this code that are retained. =============================================================================== */ +#include + #include "fpa11.h" //#include "milieu.h" //#include "softfloat.h" @@ -1331,7 +1333,11 @@ float32 float32_div( float32 a, float32 b ) aSig >>= 1; ++zExp; } - zSig = ( ( (bits64) aSig )<<32 ) / bSig; + { + bits64 tmp = ( (bits64) aSig )<<32; + do_div( tmp, bSig ); + zSig = tmp; + } if ( ( zSig & 0x3F ) == 0 ) { zSig |= ( ( (bits64) bSig ) * zSig != ( (bits64) aSig )<<32 ); } @@ -1397,7 +1403,9 @@ float32 float32_rem( float32 a, float32 b ) q = ( bSig <= aSig ); if ( q ) aSig -= bSig; if ( 0 < expDiff ) { - q = ( ( (bits64) aSig )<<32 ) / bSig; + bits64 tmp = ( (bits64) aSig )<<32; + do_div( tmp, bSig ); + q = tmp; q >>= 32 - expDiff; bSig >>= 2; aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; diff --git a/arch/arm26/Kconfig b/arch/arm26/Kconfig index 6caed90661fc..dc0c1936969b 100644 --- a/arch/arm26/Kconfig +++ b/arch/arm26/Kconfig @@ -179,6 +179,8 @@ config CMDLINE 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). +source "mm/Kconfig" + endmenu source "drivers/base/Kconfig" diff --git a/arch/arm26/boot/install.sh b/arch/arm26/boot/install.sh index c628328dd9ec..8a8399b26cf7 100644 --- a/arch/arm26/boot/install.sh +++ b/arch/arm26/boot/install.sh @@ -23,8 +23,8 @@ # User may have a custom install script -if [ -x /sbin/installkernel ]; then - exec /sbin/installkernel "$@" +if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then + exec /sbin/${CROSS_COMPILE}installkernel "$@" fi if [ "$2" = "zImage" ]; then diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index 4332ca348d51..f848e3761491 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -74,6 +74,8 @@ config PREEMPT Say Y here if you are building a kernel for a desktop, embedded or real-time system. Say N if you are unsure. +source mm/Kconfig + endmenu menu "Hardware setup" diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index 2b19372767eb..c93f95146cc2 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -74,6 +74,8 @@ config HIGHPTE with a lot of RAM, this can be wasteful of precious low memory. Setting this option will put user-space page tables in high memory. +source "mm/Kconfig" + choice prompt "uClinux kernel load address" depends on !MMU diff --git a/arch/h8300/Kconfig.cpu b/arch/h8300/Kconfig.cpu index d9dd62a565a9..a380167a13cf 100644 --- a/arch/h8300/Kconfig.cpu +++ b/arch/h8300/Kconfig.cpu @@ -180,4 +180,7 @@ config CPU_H8S config PREEMPT bool "Preemptible Kernel" default n + +source "mm/Kconfig" + endmenu diff --git a/arch/h8300/platform/h8300h/ptrace_h8300h.c b/arch/h8300/platform/h8300h/ptrace_h8300h.c index 18e51a7167d3..6ac93c05a1ae 100644 --- a/arch/h8300/platform/h8300h/ptrace_h8300h.c +++ b/arch/h8300/platform/h8300h/ptrace_h8300h.c @@ -245,12 +245,12 @@ static unsigned short *getnextpc(struct task_struct *child, unsigned short *pc) addr = h8300_get_reg(child, regno-1+PT_ER1); return (unsigned short *)addr; case relb: - if ((inst = 0x55) || isbranch(child,inst & 0x0f)) + if (inst == 0x55 || isbranch(child,inst & 0x0f)) pc = (unsigned short *)((unsigned long)pc + ((signed char)(*fetch_p))); return pc+1; /* skip myself */ case relw: - if ((inst = 0x5c) || isbranch(child,(*fetch_p & 0xf0) >> 4)) + if (inst == 0x5c || isbranch(child,(*fetch_p & 0xf0) >> 4)) pc = (unsigned short *)((unsigned long)pc + ((signed short)(*(pc+1)))); return pc+2; /* skip myself */ diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index dfd904f6883b..d4ae5f9ceae6 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -68,7 +68,6 @@ config X86_VOYAGER config X86_NUMAQ bool "NUMAQ (IBM/Sequent)" - select DISCONTIGMEM select NUMA help This option is used for getting Linux to run on a (IBM/Sequent) NUMA @@ -783,26 +782,49 @@ comment "NUMA (NUMA-Q) requires SMP, 64GB highmem support" comment "NUMA (Summit) requires SMP, 64GB highmem support, ACPI" depends on X86_SUMMIT && (!HIGHMEM64G || !ACPI) -config DISCONTIGMEM - bool - depends on NUMA - default y - config HAVE_ARCH_BOOTMEM_NODE bool depends on NUMA default y -config HAVE_MEMORY_PRESENT +config ARCH_HAVE_MEMORY_PRESENT bool depends on DISCONTIGMEM default y config NEED_NODE_MEMMAP_SIZE bool - depends on DISCONTIGMEM + depends on DISCONTIGMEM || SPARSEMEM default y +config HAVE_ARCH_ALLOC_REMAP + bool + depends on NUMA + default y + +config ARCH_DISCONTIGMEM_ENABLE + def_bool y + depends on NUMA + +config ARCH_DISCONTIGMEM_DEFAULT + def_bool y + depends on NUMA + +config ARCH_SPARSEMEM_ENABLE + def_bool y + depends on NUMA + +config ARCH_SELECT_MEMORY_MODEL + def_bool y + depends on ARCH_SPARSEMEM_ENABLE + +source "mm/Kconfig" + +config HAVE_ARCH_EARLY_PFN_TO_NID + bool + default y + depends on NUMA + config HIGHPTE bool "Allocate 3rd-level pagetables from highmem" depends on HIGHMEM4G || HIGHMEM64G @@ -939,6 +961,8 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +source kernel/Kconfig.hz + endmenu diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 1c36ca332a96..bf7c9ba709f3 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -17,6 +17,13 @@ # 20050320 Kianusch Sayah Karadji # Added support for GEODE CPU +HAS_BIARCH := $(call cc-option-yn, -m32) +ifeq ($(HAS_BIARCH),y) +AS := $(AS) --32 +LD := $(LD) -m elf_i386 +CC := $(CC) -m32 +endif + LDFLAGS := -m elf_i386 OBJCOPYFLAGS := -O binary -R .note -R .comment -S LDFLAGS_vmlinux := diff --git a/arch/i386/boot/install.sh b/arch/i386/boot/install.sh index 90f2452b3b9e..f17b40dfc0f4 100644 --- a/arch/i386/boot/install.sh +++ b/arch/i386/boot/install.sh @@ -21,8 +21,8 @@ # 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 +if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi +if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then exec /sbin/${CROSS_COMPILE}installkernel "$@"; fi # Default install - same as make zlilo diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index d509836b70c3..8d993fa71754 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -1133,7 +1133,7 @@ inline void smp_local_timer_interrupt(struct pt_regs * regs) } #ifdef CONFIG_SMP - update_process_times(user_mode(regs)); + update_process_times(user_mode_vm(regs)); #endif } diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c index d199e525680a..b9954248d0aa 100644 --- a/arch/i386/kernel/cpu/common.c +++ b/arch/i386/kernel/cpu/common.c @@ -635,7 +635,7 @@ void __init cpu_init (void) /* Clear all 6 debug registers: */ -#define CD(register) __asm__("movl %0,%%db" #register ::"r"(0) ); +#define CD(register) set_debugreg(0, register) CD(0); CD(1); CD(2); CD(3); /* no db4 and db5 */; CD(6); CD(7); diff --git a/arch/i386/kernel/cpu/mtrr/main.c b/arch/i386/kernel/cpu/mtrr/main.c index e1c2042b9b7e..d66b09e0c820 100644 --- a/arch/i386/kernel/cpu/mtrr/main.c +++ b/arch/i386/kernel/cpu/mtrr/main.c @@ -375,6 +375,19 @@ int mtrr_add_page(unsigned long base, unsigned long size, return error; } +static int mtrr_check(unsigned long base, unsigned long size) +{ + if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { + printk(KERN_WARNING + "mtrr: size and base must be multiples of 4 kiB\n"); + printk(KERN_DEBUG + "mtrr: size: 0x%lx base: 0x%lx\n", size, base); + dump_stack(); + return -1; + } + return 0; +} + /** * mtrr_add - Add a memory type region * @base: Physical base address of region @@ -415,11 +428,8 @@ int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment) { - if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { - printk(KERN_WARNING "mtrr: size and base must be multiples of 4 kiB\n"); - printk(KERN_DEBUG "mtrr: size: 0x%lx base: 0x%lx\n", size, base); + if (mtrr_check(base, size)) return -EINVAL; - } return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment); } @@ -511,11 +521,8 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size) int mtrr_del(int reg, unsigned long base, unsigned long size) { - if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { - printk(KERN_INFO "mtrr: size and base must be multiples of 4 kiB\n"); - printk(KERN_DEBUG "mtrr: size: 0x%lx base: 0x%lx\n", size, base); + if (mtrr_check(base, size)) return -EINVAL; - } return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); } diff --git a/arch/i386/kernel/cpu/proc.c b/arch/i386/kernel/cpu/proc.c index 7323c19f354e..8bd77d948a84 100644 --- a/arch/i386/kernel/cpu/proc.c +++ b/arch/i386/kernel/cpu/proc.c @@ -86,7 +86,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, "stepping\t: unknown\n"); if ( cpu_has(c, X86_FEATURE_TSC) ) { - seq_printf(m, "cpu MHz\t\t: %lu.%03lu\n", + seq_printf(m, "cpu MHz\t\t: %u.%03u\n", cpu_khz / 1000, (cpu_khz % 1000)); } diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 903190a4b3ff..180f070d03cb 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -1,97 +1,17 @@ #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 - -extern void dump_thread(struct pt_regs *, struct user *); -extern spinlock_t rtc_lock; /* This is definitely a GPL-only symbol */ EXPORT_SYMBOL_GPL(cpu_gdt_table); -#if defined(CONFIG_APM_MODULE) -extern void machine_real_restart(unsigned char *, int); -EXPORT_SYMBOL(machine_real_restart); -extern void default_idle(void); -EXPORT_SYMBOL(default_idle); -#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 cpu_khz; -extern unsigned long get_cmos_time(void); - -/* platform dependent support */ -EXPORT_SYMBOL(boot_cpu_data); -#ifdef CONFIG_DISCONTIGMEM -EXPORT_SYMBOL(node_data); -EXPORT_SYMBOL(physnode_map); -#endif -#ifdef CONFIG_X86_NUMAQ -EXPORT_SYMBOL(xquad_portio); -#endif -EXPORT_SYMBOL(dump_thread); -EXPORT_SYMBOL(dump_fpu); -EXPORT_SYMBOL_GPL(kernel_fpu_begin); -EXPORT_SYMBOL(__ioremap); -EXPORT_SYMBOL(ioremap_nocache); -EXPORT_SYMBOL(iounmap); -EXPORT_SYMBOL(kernel_thread); -EXPORT_SYMBOL(pm_idle); -EXPORT_SYMBOL(pm_power_off); -EXPORT_SYMBOL(get_cmos_time); -EXPORT_SYMBOL(cpu_khz); -EXPORT_SYMBOL(apm_info); - 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); -/* Delay loops */ -EXPORT_SYMBOL(__ndelay); -EXPORT_SYMBOL(__udelay); -EXPORT_SYMBOL(__delay); -EXPORT_SYMBOL(__const_udelay); EXPORT_SYMBOL(__get_user_1); EXPORT_SYMBOL(__get_user_2); @@ -105,87 +25,11 @@ EXPORT_SYMBOL(__put_user_8); EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); -EXPORT_SYMBOL(strncpy_from_user); -EXPORT_SYMBOL(__strncpy_from_user); -EXPORT_SYMBOL(clear_user); -EXPORT_SYMBOL(__clear_user); -EXPORT_SYMBOL(__copy_from_user_ll); -EXPORT_SYMBOL(__copy_to_user_ll); -EXPORT_SYMBOL(strnlen_user); - -EXPORT_SYMBOL(dma_alloc_coherent); -EXPORT_SYMBOL(dma_free_coherent); - -#ifdef CONFIG_PCI -EXPORT_SYMBOL(pci_mem_start); -#endif - -#ifdef CONFIG_PCI_BIOS -EXPORT_SYMBOL(pcibios_set_irq_routing); -EXPORT_SYMBOL(pcibios_get_irq_routing_table); -#endif - -#ifdef CONFIG_X86_USE_3DNOW -EXPORT_SYMBOL(_mmx_memcpy); -EXPORT_SYMBOL(mmx_clear_page); -EXPORT_SYMBOL(mmx_copy_page); -#endif - -#ifdef CONFIG_X86_HT -EXPORT_SYMBOL(smp_num_siblings); -EXPORT_SYMBOL(cpu_sibling_map); -#endif - #ifdef CONFIG_SMP -EXPORT_SYMBOL(cpu_data); -EXPORT_SYMBOL(cpu_online_map); -EXPORT_SYMBOL(cpu_callout_map); +extern void FASTCALL( __write_lock_failed(rwlock_t *rw)); +extern void FASTCALL( __read_lock_failed(rwlock_t *rw)); EXPORT_SYMBOL(__write_lock_failed); EXPORT_SYMBOL(__read_lock_failed); - -/* Global SMP stuff */ -EXPORT_SYMBOL(smp_call_function); - -/* TLB flushing */ -EXPORT_SYMBOL(flush_tlb_page); -#endif - -#ifdef CONFIG_X86_IO_APIC -EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); -#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); - -EXPORT_SYMBOL_GPL(set_nmi_callback); -EXPORT_SYMBOL_GPL(unset_nmi_callback); - -EXPORT_SYMBOL(register_die_notifier); -#ifdef CONFIG_HAVE_DEC_LOCK -EXPORT_SYMBOL(_atomic_dec_and_lock); -#endif - -EXPORT_SYMBOL(__PAGE_KERNEL); - -#ifdef CONFIG_HIGHMEM -EXPORT_SYMBOL(kmap); -EXPORT_SYMBOL(kunmap); -EXPORT_SYMBOL(kmap_atomic); -EXPORT_SYMBOL(kunmap_atomic); -EXPORT_SYMBOL(kmap_atomic_to_page); -#endif - -#if defined(CONFIG_X86_SPEEDSTEP_SMI) || defined(CONFIG_X86_SPEEDSTEP_SMI_MODULE) -EXPORT_SYMBOL(ist_info); #endif EXPORT_SYMBOL(csum_partial); diff --git a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c index c55e037f08f7..b817168d9c62 100644 --- a/arch/i386/kernel/i387.c +++ b/arch/i386/kernel/i387.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -79,6 +80,7 @@ void kernel_fpu_begin(void) } clts(); } +EXPORT_SYMBOL_GPL(kernel_fpu_begin); void restore_fpu( struct task_struct *tsk ) { @@ -526,6 +528,7 @@ int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu ) return fpvalid; } +EXPORT_SYMBOL(dump_fpu); int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu) { diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 7a324e8b86f9..08540bc4ba3e 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -31,7 +31,7 @@ #include #include #include - +#include #include #include #include @@ -812,6 +812,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) } return best_guess; } +EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); /* * This function currently is only a helper for the i386 smp boot process where @@ -1658,6 +1659,12 @@ static void __init setup_ioapic_ids_from_mpc(void) unsigned char old_id; unsigned long flags; + /* + * Don't check I/O APIC IDs for xAPIC systems. They have + * no meaning without the serial APIC bus. + */ + if (!(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && boot_cpu_data.x86 < 15)) + return; /* * This is broken; anything with a real cpu count has to * circumvent this idiocy regardless. @@ -1684,10 +1691,6 @@ static void __init setup_ioapic_ids_from_mpc(void) mp_ioapics[apic].mpc_apicid = reg_00.bits.ID; } - /* Don't check I/O APIC IDs for some xAPIC systems. They have - * no meaning without the serial APIC bus. */ - if (NO_IOAPIC_CHECK) - continue; /* * Sanity check, is the ID really free? Every APIC in a * system must have a unique ID or we get lots of nice diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index 59ff9b455069..3762f6b35ab2 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c @@ -23,6 +23,9 @@ * Rusty Russell). * 2004-July Suparna Bhattacharya added jumper probes * interface to access function arguments. + * 2005-May Hien Nguyen , Jim Keniston + * and Prasanna S Panchamukhi + * added function-return probes. */ #include @@ -30,15 +33,14 @@ #include #include #include +#include #include #include -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 - static struct kprobe *current_kprobe; static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_status_prev, kprobe_old_eflags_prev, kprobe_saved_eflags_prev; static struct pt_regs jprobe_saved_regs; static long *jprobe_saved_esp; /* copy of the kernel stack at the probe fire time */ @@ -68,16 +70,50 @@ int arch_prepare_kprobe(struct kprobe *p) void arch_copy_kprobe(struct kprobe *p) { memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); + p->opcode = *p->addr; +} + +void arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); +} + +void arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); } void arch_remove_kprobe(struct kprobe *p) { } -static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +static inline void save_previous_kprobe(void) { - *p->addr = p->opcode; - regs->eip = (unsigned long)p->addr; + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; + kprobe_old_eflags_prev = kprobe_old_eflags; + kprobe_saved_eflags_prev = kprobe_saved_eflags; +} + +static inline void restore_previous_kprobe(void) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; + kprobe_old_eflags = kprobe_old_eflags_prev; + kprobe_saved_eflags = kprobe_saved_eflags_prev; +} + +static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + current_kprobe = p; + kprobe_saved_eflags = kprobe_old_eflags + = (regs->eflags & (TF_MASK | IF_MASK)); + if (is_IF_modifier(p->opcode)) + kprobe_saved_eflags &= ~IF_MASK; } static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) @@ -91,6 +127,50 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) regs->eip = (unsigned long)&p->ainsn.insn; } +struct task_struct *arch_get_kprobe_task(void *ptr) +{ + return ((struct thread_info *) (((unsigned long) ptr) & + (~(THREAD_SIZE -1))))->task; +} + +void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) +{ + unsigned long *sara = (unsigned long *)®s->esp; + struct kretprobe_instance *ri; + static void *orig_ret_addr; + + /* + * Save the return address when the return probe hits + * the first time, and use it to populate the (krprobe + * instance)->ret_addr for subsequent return probes at + * the same addrress since stack address would have + * the kretprobe_trampoline by then. + */ + if (((void*) *sara) != kretprobe_trampoline) + orig_ret_addr = (void*) *sara; + + if ((ri = get_free_rp_inst(rp)) != NULL) { + ri->rp = rp; + ri->stack_addr = sara; + ri->ret_addr = orig_ret_addr; + add_rp_inst(ri); + /* Replace the return addr with trampoline addr */ + *sara = (unsigned long) &kretprobe_trampoline; + } else { + rp->nmissed++; + } +} + +void arch_kprobe_flush_task(struct task_struct *tk) +{ + struct kretprobe_instance *ri; + while ((ri = get_rp_inst_tsk(tk)) != NULL) { + *((unsigned long *)(ri->stack_addr)) = + (unsigned long) ri->ret_addr; + recycle_rp_inst(ri); + } +} + /* * Interrupts are disabled on entry as trap3 is an interrupt gate and they * remain disabled thorough out this function. @@ -127,8 +207,18 @@ static int kprobe_handler(struct pt_regs *regs) unlock_kprobes(); goto no_kprobe; } - disarm_kprobe(p, regs); - ret = 1; + /* We have reentered the kprobe_handler(), since + * another probe was hit while within the handler. + * We here save the original kprobes variables and + * just single step on the instruction of the new probe + * without calling any user handlers. + */ + save_previous_kprobe(); + set_current_kprobe(p, regs); + p->nmissed++; + prepare_singlestep(p, regs); + kprobe_status = KPROBE_REENTER; + return 1; } else { p = current_kprobe; if (p->break_handler && p->break_handler(p, regs)) { @@ -163,11 +253,7 @@ static int kprobe_handler(struct pt_regs *regs) } kprobe_status = KPROBE_HIT_ACTIVE; - current_kprobe = p; - kprobe_saved_eflags = kprobe_old_eflags - = (regs->eflags & (TF_MASK | IF_MASK)); - if (is_IF_modifier(p->opcode)) - kprobe_saved_eflags &= ~IF_MASK; + set_current_kprobe(p, regs); if (p->pre_handler && p->pre_handler(p, regs)) /* handler has already set things up, so skip ss setup */ @@ -183,6 +269,55 @@ no_kprobe: return ret; } +/* + * For function-return probes, init_kprobes() establishes a probepoint + * here. When a retprobed function returns, this probe is hit and + * trampoline_probe_handler() runs, calling the kretprobe's handler. + */ + void kretprobe_trampoline_holder(void) + { + asm volatile ( ".global kretprobe_trampoline\n" + "kretprobe_trampoline: \n" + "nop\n"); + } + +/* + * Called when we hit the probe point at kretprobe_trampoline + */ +int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct task_struct *tsk; + struct kretprobe_instance *ri; + struct hlist_head *head; + struct hlist_node *node; + unsigned long *sara = ((unsigned long *) ®s->esp) - 1; + + tsk = arch_get_kprobe_task(sara); + head = kretprobe_inst_table_head(tsk); + + hlist_for_each_entry(ri, node, head, hlist) { + if (ri->stack_addr == sara && ri->rp) { + if (ri->rp->handler) + ri->rp->handler(ri, regs); + } + } + return 0; +} + +void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + struct kretprobe_instance *ri; + /* RA already popped */ + unsigned long *sara = ((unsigned long *)®s->esp) - 1; + + while ((ri = get_rp_inst(sara))) { + regs->eip = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri); + } + regs->eflags &= ~TF_MASK; +} + /* * Called after single-stepping. p->addr is the address of the * instruction whose first byte has been replaced by the "int 3" @@ -263,13 +398,22 @@ static inline int post_kprobe_handler(struct pt_regs *regs) if (!kprobe_running()) return 0; - if (current_kprobe->post_handler) + if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { + kprobe_status = KPROBE_HIT_SSDONE; current_kprobe->post_handler(current_kprobe, regs, 0); + } - resume_execution(current_kprobe, regs); + if (current_kprobe->post_handler != trampoline_post_handler) + resume_execution(current_kprobe, regs); regs->eflags |= kprobe_saved_eflags; + /*Restore back the original saved kprobes variables and continue. */ + if (kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(); + goto out; + } unlock_kprobes(); +out: preempt_enable_no_resched(); /* diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 1347ab4939e7..383a11600d2c 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -914,7 +914,10 @@ void __init mp_register_ioapic ( mp_ioapics[idx].mpc_apicaddr = address; set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address); - mp_ioapics[idx].mpc_apicid = io_apic_get_unique_id(idx, id); + if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && (boot_cpu_data.x86 < 15)) + mp_ioapics[idx].mpc_apicid = io_apic_get_unique_id(idx, id); + else + mp_ioapics[idx].mpc_apicid = id; mp_ioapics[idx].mpc_apicver = io_apic_get_version(idx); /* @@ -1055,11 +1058,20 @@ void __init mp_config_acpi_legacy_irqs (void) } } +#define MAX_GSI_NUM 4096 + int mp_register_gsi (u32 gsi, int edge_level, int active_high_low) { int ioapic = -1; int ioapic_pin = 0; int idx, bit = 0; + static int pci_irq = 16; + /* + * Mapping between Global System Interrups, which + * represent all possible interrupts, and IRQs + * assigned to actual devices. + */ + static int gsi_to_irq[MAX_GSI_NUM]; #ifdef CONFIG_ACPI_BUS /* Don't set up the ACPI SCI because it's already set up */ @@ -1094,11 +1106,26 @@ int mp_register_gsi (u32 gsi, int edge_level, int active_high_low) if ((1< #include -#include -#include +#include #include #include "mach_traps.h" @@ -324,6 +323,16 @@ static void clear_msr_range(unsigned int base, unsigned int n) wrmsr(base+i, 0, 0); } +static inline void write_watchdog_counter(const char *descr) +{ + u64 count = (u64)cpu_khz * 1000; + + do_div(count, nmi_hz); + if(descr) + Dprintk("setting %s to -0x%08Lx\n", descr, count); + wrmsrl(nmi_perfctr_msr, 0 - count); +} + static void setup_k7_watchdog(void) { unsigned int evntsel; @@ -339,8 +348,7 @@ static void setup_k7_watchdog(void) | 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); + write_watchdog_counter("K7_PERFCTR0"); apic_write(APIC_LVTPC, APIC_DM_NMI); evntsel |= K7_EVNTSEL_ENABLE; wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); @@ -361,8 +369,7 @@ static void setup_p6_watchdog(void) | P6_NMI_EVENT; wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); - Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); - wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); + write_watchdog_counter("P6_PERFCTR0"); apic_write(APIC_LVTPC, APIC_DM_NMI); evntsel |= P6_EVNTSEL0_ENABLE; wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); @@ -402,8 +409,7 @@ static int setup_p4_watchdog(void) wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); - Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000)); - wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1); + write_watchdog_counter("P4_IQ_COUNTER0"); apic_write(APIC_LVTPC, APIC_DM_NMI); wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); return 1; @@ -518,7 +524,7 @@ void nmi_watchdog_tick (struct pt_regs * regs) * other P6 variant */ apic_write(APIC_LVTPC, APIC_DM_NMI); } - wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); + write_watchdog_counter(NULL); } } diff --git a/arch/i386/kernel/pci-dma.c b/arch/i386/kernel/pci-dma.c index 4de2e03c7b45..1e51427cc9eb 100644 --- a/arch/i386/kernel/pci-dma.c +++ b/arch/i386/kernel/pci-dma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include struct dma_coherent_mem { @@ -54,6 +55,7 @@ void *dma_alloc_coherent(struct device *dev, size_t size, } return ret; } +EXPORT_SYMBOL(dma_alloc_coherent); void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle) @@ -68,6 +70,7 @@ void dma_free_coherent(struct device *dev, size_t size, } else free_pages((unsigned long)vaddr, order); } +EXPORT_SYMBOL(dma_free_coherent); int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dma_addr_t device_addr, size_t size, int flags) diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 96e3ea6b17c7..aea2ce1145df 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk) * Powermanagement idle function, if any.. */ void (*pm_idle)(void); +EXPORT_SYMBOL(pm_idle); static DEFINE_PER_CPU(unsigned int, cpu_idle_state); void disable_hlt(void) @@ -105,6 +107,9 @@ void default_idle(void) cpu_relax(); } } +#ifdef CONFIG_APM_MODULE +EXPORT_SYMBOL(default_idle); +#endif /* * On SMP it's slightly faster (but much more power-consuming!) @@ -262,7 +267,7 @@ void show_regs(struct pt_regs * regs) printk("EIP: %04x:[<%08lx>] CPU: %d\n",0xffff & regs->xcs,regs->eip, smp_processor_id()); print_symbol("EIP is at %s\n", regs->eip); - if (regs->xcs & 3) + if (user_mode(regs)) printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp); printk(" EFLAGS: %08lx %s (%s)\n", regs->eflags, print_tainted(), system_utsname.release); @@ -325,6 +330,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) /* Ok, create the new process.. */ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); } +EXPORT_SYMBOL(kernel_thread); /* * Free current thread data structures etc.. @@ -334,6 +340,13 @@ void exit_thread(void) struct task_struct *tsk = current; struct thread_struct *t = &tsk->thread; + /* + * Remove function-return probe instances associated with this task + * and put them back on the free list. Do not insert an exit probe for + * this function, it will be disabled by kprobe_flush_task if you do. + */ + kprobe_flush_task(tsk); + /* The process may have allocated an io port bitmap... nuke it. */ if (unlikely(NULL != t->io_bitmap_ptr)) { int cpu = get_cpu(); @@ -357,6 +370,13 @@ void flush_thread(void) { struct task_struct *tsk = current; + /* + * Remove function-return probe instances associated with this task + * and put them back on the free list. Do not insert an exit probe for + * this function, it will be disabled by kprobe_flush_task if you do. + */ + kprobe_flush_task(tsk); + memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); /* @@ -508,6 +528,7 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->u_fpvalid = dump_fpu (regs, &dump->i387); } +EXPORT_SYMBOL(dump_thread); /* * Capture the user space registers if the task is not running (in user space) @@ -627,13 +648,13 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas * Now maybe reload the debug registers */ if (unlikely(next->debugreg[7])) { - loaddebug(next, 0); - loaddebug(next, 1); - loaddebug(next, 2); - loaddebug(next, 3); + set_debugreg(current->thread.debugreg[0], 0); + set_debugreg(current->thread.debugreg[1], 1); + set_debugreg(current->thread.debugreg[2], 2); + set_debugreg(current->thread.debugreg[3], 3); /* no 4 and 5 */ - loaddebug(next, 6); - loaddebug(next, 7); + set_debugreg(current->thread.debugreg[6], 6); + set_debugreg(current->thread.debugreg[7], 7); } if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) @@ -731,6 +752,7 @@ unsigned long get_wchan(struct task_struct *p) } while (count++ < 16); return 0; } +EXPORT_SYMBOL(get_wchan); /* * sys_alloc_thread_area: get a yet unused TLS descriptor index. diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index e34f651fa13c..0da59b42843c 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -668,7 +668,7 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) info.si_code = TRAP_BRKPT; /* User-mode eip? */ - info.si_addr = user_mode(regs) ? (void __user *) regs->eip : NULL; + info.si_addr = user_mode_vm(regs) ? (void __user *) regs->eip : NULL; /* Send us the fakey SIGTRAP */ force_sig_info(SIGTRAP, &info, tsk); diff --git a/arch/i386/kernel/reboot.c b/arch/i386/kernel/reboot.c index 6dc27eb70ee7..db912209a8d3 100644 --- a/arch/i386/kernel/reboot.c +++ b/arch/i386/kernel/reboot.c @@ -2,6 +2,7 @@ * linux/arch/i386/kernel/reboot.c */ +#include #include #include #include @@ -19,6 +20,7 @@ * Power off function, if any */ void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); static int reboot_mode; static int reboot_thru_bios; @@ -295,6 +297,9 @@ void machine_real_restart(unsigned char *code, int length) : : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); } +#ifdef CONFIG_APM_MODULE +EXPORT_SYMBOL(machine_real_restart); +#endif void machine_restart(char * __unused) { diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 2bfbddebdbf8..30406fd0b64c 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -23,8 +23,10 @@ * This file handles the architecture-dependent parts of initialization */ +#include #include #include +#include #include #include #include @@ -73,6 +75,7 @@ EXPORT_SYMBOL(efi_enabled); struct cpuinfo_x86 new_cpu_data __initdata = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; /* common cpu data for all cpus */ struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; +EXPORT_SYMBOL(boot_cpu_data); unsigned long mmu_cr4_features; @@ -90,12 +93,18 @@ extern acpi_interrupt_flags acpi_sci_flags; /* for MCA, but anyone else can use it if they want */ unsigned int machine_id; +#ifdef CONFIG_MCA +EXPORT_SYMBOL(machine_id); +#endif 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; +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pci_mem_start); +#endif /* Boot loader ID as an integer, for the benefit of proc_dointvec */ int bootloader_type; @@ -107,14 +116,26 @@ static unsigned int highmem_pages = -1; * Setup options */ struct drive_info_struct { char dummy[32]; } drive_info; +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || \ + defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) +EXPORT_SYMBOL(drive_info); +#endif struct screen_info screen_info; +#ifdef CONFIG_VT +EXPORT_SYMBOL(screen_info); +#endif struct apm_info apm_info; +EXPORT_SYMBOL(apm_info); struct sys_desc_table_struct { unsigned short length; unsigned char table[0]; }; struct edid_info edid_info; struct ist_info ist_info; +#if defined(CONFIG_X86_SPEEDSTEP_SMI) || \ + defined(CONFIG_X86_SPEEDSTEP_SMI_MODULE) +EXPORT_SYMBOL(ist_info); +#endif struct e820map e820; extern void early_cpu_init(void); @@ -1022,7 +1043,7 @@ static void __init reserve_ebda_region(void) reserve_bootmem(addr, PAGE_SIZE); } -#ifndef CONFIG_DISCONTIGMEM +#ifndef CONFIG_NEED_MULTIPLE_NODES void __init setup_bootmem_allocator(void); static unsigned long __init setup_memory(void) { @@ -1072,9 +1093,9 @@ void __init zone_sizes_init(void) free_area_init(zones_size); } #else -extern unsigned long setup_memory(void); +extern unsigned long __init setup_memory(void); extern void zone_sizes_init(void); -#endif /* !CONFIG_DISCONTIGMEM */ +#endif /* !CONFIG_NEED_MULTIPLE_NODES */ void __init setup_bootmem_allocator(void) { @@ -1475,6 +1496,7 @@ void __init setup_arch(char **cmdline_p) #endif paging_init(); remapped_pgdat_init(); + sparse_init(); zone_sizes_init(); /* diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index ea46d028af08..b9b8f4e20fad 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -346,8 +346,8 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) extern void __user __kernel_sigreturn; extern void __user __kernel_rt_sigreturn; -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) +static int setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) { void __user *restorer; struct sigframe __user *frame; @@ -429,13 +429,14 @@ static void setup_frame(int sig, struct k_sigaction *ka, current->comm, current->pid, frame, regs->eip, frame->pretcode); #endif - return; + return 1; give_sigsegv: force_sigsegv(sig, current); + return 0; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { void __user *restorer; @@ -522,20 +523,23 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, current->comm, current->pid, frame, regs->eip, frame->pretcode); #endif - return; + return 1; give_sigsegv: force_sigsegv(sig, current); + return 0; } /* * OK, we're invoking a handler */ -static void +static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) { + int ret; + /* Are we from a system call? */ if (regs->orig_eax >= 0) { /* If so, check system call restarting.. */ @@ -569,17 +573,19 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); - if (!(ka->sa.sa_flags & SA_NODEFER)) { + if (ret && !(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); } + + return ret; } /* @@ -599,7 +605,7 @@ int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset) * kernel mode. Just return without doing anything * if so. */ - if ((regs->xcs & 3) != 3) + if (!user_mode(regs)) return 1; if (current->flags & PF_FREEZE) { @@ -618,12 +624,11 @@ int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset) * inside the kernel. */ if (unlikely(current->thread.debugreg[7])) { - loaddebug(¤t->thread, 7); + set_debugreg(current->thread.debugreg[7], 7); } /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, &ka, oldset, regs); - return 1; + return handle_signal(signr, &info, &ka, oldset, regs); } no_signal: diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 6223c33ac91c..68be7d0c7238 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -452,6 +453,7 @@ void flush_tlb_page(struct vm_area_struct * vma, unsigned long va) preempt_enable(); } +EXPORT_SYMBOL(flush_tlb_page); static void do_flush_tlb_all(void* info) { @@ -547,6 +549,7 @@ int smp_call_function (void (*func) (void *info), void *info, int nonatomic, return 0; } +EXPORT_SYMBOL(smp_call_function); static void stop_this_cpu (void * dummy) { diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index bc1bb6919e6a..c20d96d5c15c 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -60,6 +60,9 @@ static int __initdata smp_b_stepping; /* Number of siblings per CPU package */ int smp_num_siblings = 1; +#ifdef CONFIG_X86_HT +EXPORT_SYMBOL(smp_num_siblings); +#endif int phys_proc_id[NR_CPUS]; /* Package ID of each logical CPU */ EXPORT_SYMBOL(phys_proc_id); int cpu_core_id[NR_CPUS]; /* Core ID of each logical CPU */ @@ -67,13 +70,16 @@ EXPORT_SYMBOL(cpu_core_id); /* bitmap of online cpus */ cpumask_t cpu_online_map; +EXPORT_SYMBOL(cpu_online_map); cpumask_t cpu_callin_map; cpumask_t cpu_callout_map; +EXPORT_SYMBOL(cpu_callout_map); static cpumask_t smp_commenced_mask; /* Per CPU bogomips and other parameters */ struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; +EXPORT_SYMBOL(cpu_data); u8 x86_cpu_to_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = 0xff }; @@ -199,7 +205,7 @@ static void __init synchronize_tsc_bp (void) unsigned long long t0; unsigned long long sum, avg; long long delta; - unsigned long one_usec; + unsigned int one_usec; int buggy = 0; printk(KERN_INFO "checking TSC synchronization across %u CPUs: ", num_booting_cpus()); @@ -885,8 +891,14 @@ static void smp_tune_scheduling (void) static int boot_cpu_logical_apicid; /* Where the IO area was mapped on multiquad, always 0 otherwise */ void *xquad_portio; +#ifdef CONFIG_X86_NUMAQ +EXPORT_SYMBOL(xquad_portio); +#endif cpumask_t cpu_sibling_map[NR_CPUS] __cacheline_aligned; +#ifdef CONFIG_X86_HT +EXPORT_SYMBOL(cpu_sibling_map); +#endif cpumask_t cpu_core_map[NR_CPUS] __cacheline_aligned; EXPORT_SYMBOL(cpu_core_map); diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index a0dcb7c87c30..e68d9fdb0759 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -77,11 +77,13 @@ u64 jiffies_64 = INITIAL_JIFFIES; EXPORT_SYMBOL(jiffies_64); -unsigned long cpu_khz; /* Detected as we calibrate the TSC */ +unsigned int cpu_khz; /* Detected as we calibrate the TSC */ +EXPORT_SYMBOL(cpu_khz); extern unsigned long wall_jiffies; DEFINE_SPINLOCK(rtc_lock); +EXPORT_SYMBOL(rtc_lock); DEFINE_SPINLOCK(i8253_lock); EXPORT_SYMBOL(i8253_lock); @@ -324,6 +326,8 @@ unsigned long get_cmos_time(void) return retval; } +EXPORT_SYMBOL(get_cmos_time); + static void sync_cmos_clock(unsigned long dummy); static struct timer_list sync_cmos_timer = diff --git a/arch/i386/kernel/timers/common.c b/arch/i386/kernel/timers/common.c index 8e201219f525..37353bd31803 100644 --- a/arch/i386/kernel/timers/common.c +++ b/arch/i386/kernel/timers/common.c @@ -139,6 +139,15 @@ bad_calibration: } #endif + +unsigned long read_timer_tsc(void) +{ + unsigned long retval; + rdtscl(retval); + return retval; +} + + /* calculate cpu_khz */ void init_cpu_khz(void) { @@ -154,7 +163,8 @@ void init_cpu_khz(void) :"=a" (cpu_khz), "=d" (edx) :"r" (tsc_quotient), "0" (eax), "1" (edx)); - printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); + printk("Detected %u.%03u MHz processor.\n", + cpu_khz / 1000, cpu_khz % 1000); } } } diff --git a/arch/i386/kernel/timers/timer.c b/arch/i386/kernel/timers/timer.c index a3d6a288088b..7e39ed8e33f8 100644 --- a/arch/i386/kernel/timers/timer.c +++ b/arch/i386/kernel/timers/timer.c @@ -64,3 +64,12 @@ struct timer_opts* __init select_timer(void) panic("select_timer: Cannot find a suitable timer\n"); return NULL; } + +int read_current_timer(unsigned long *timer_val) +{ + if (cur_timer->read_timer) { + *timer_val = cur_timer->read_timer(); + return 0; + } + return -1; +} diff --git a/arch/i386/kernel/timers/timer_hpet.c b/arch/i386/kernel/timers/timer_hpet.c index f778f471a09a..d766e0963ac1 100644 --- a/arch/i386/kernel/timers/timer_hpet.c +++ b/arch/i386/kernel/timers/timer_hpet.c @@ -158,7 +158,7 @@ static int __init init_hpet(char* override) { unsigned long eax=0, edx=1000; ASM_DIV64_REG(cpu_khz, edx, tsc_quotient, eax, edx); - printk("Detected %lu.%03lu MHz processor.\n", + printk("Detected %u.%03u MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); } set_cyc2ns_scale(cpu_khz/1000); @@ -186,6 +186,7 @@ static struct timer_opts timer_hpet = { .get_offset = get_offset_hpet, .monotonic_clock = monotonic_clock_hpet, .delay = delay_hpet, + .read_timer = read_timer_tsc, }; struct init_timer_opts __initdata timer_hpet_init = { diff --git a/arch/i386/kernel/timers/timer_pm.c b/arch/i386/kernel/timers/timer_pm.c index d77f22030fe6..4ef20e663498 100644 --- a/arch/i386/kernel/timers/timer_pm.c +++ b/arch/i386/kernel/timers/timer_pm.c @@ -246,6 +246,7 @@ static struct timer_opts timer_pmtmr = { .get_offset = get_offset_pmtmr, .monotonic_clock = monotonic_clock_pmtmr, .delay = delay_pmtmr, + .read_timer = read_timer_tsc, }; struct init_timer_opts __initdata timer_pmtmr_init = { diff --git a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c index 180444d87824..54c36b182021 100644 --- a/arch/i386/kernel/timers/timer_tsc.c +++ b/arch/i386/kernel/timers/timer_tsc.c @@ -256,7 +256,7 @@ static unsigned long loops_per_jiffy_ref = 0; #ifndef CONFIG_SMP static unsigned long fast_gettimeoffset_ref = 0; -static unsigned long cpu_khz_ref = 0; +static unsigned int cpu_khz_ref = 0; #endif static int @@ -323,7 +323,7 @@ static inline void cpufreq_delayed_get(void) { return; } int recalibrate_cpu_khz(void) { #ifndef CONFIG_SMP - unsigned long cpu_khz_old = cpu_khz; + unsigned int cpu_khz_old = cpu_khz; if (cpu_has_tsc) { init_cpu_khz(); @@ -534,7 +534,8 @@ static int __init init_tsc(char* override) :"=a" (cpu_khz), "=d" (edx) :"r" (tsc_quotient), "0" (eax), "1" (edx)); - printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); + printk("Detected %u.%03u MHz processor.\n", + cpu_khz / 1000, cpu_khz % 1000); } set_cyc2ns_scale(cpu_khz/1000); return 0; @@ -572,6 +573,7 @@ static struct timer_opts timer_tsc = { .get_offset = get_offset_tsc, .monotonic_clock = monotonic_clock_tsc, .delay = delay_tsc, + .read_timer = read_timer_tsc, }; struct init_timer_opts __initdata timer_tsc_init = { diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 83c579e82a81..e4d4e2162c7a 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -104,6 +104,7 @@ int register_die_notifier(struct notifier_block *nb) spin_unlock_irqrestore(&die_notifier_lock, flags); return err; } +EXPORT_SYMBOL(register_die_notifier); static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) { @@ -209,7 +210,7 @@ void show_registers(struct pt_regs *regs) esp = (unsigned long) (®s->esp); ss = __KERNEL_DS; - if (regs->xcs & 3) { + if (user_mode(regs)) { in_kernel = 0; esp = regs->esp; ss = regs->xss & 0xffff; @@ -265,7 +266,7 @@ static void handle_BUG(struct pt_regs *regs) char c; unsigned long eip; - if (regs->xcs & 3) + if (user_mode(regs)) goto no_bug; /* Not in kernel */ eip = regs->eip; @@ -353,7 +354,7 @@ void die(const char * str, struct pt_regs * regs, long err) static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) { - if (!(regs->eflags & VM_MASK) && !(3 & regs->xcs)) + if (!user_mode_vm(regs)) die(str, regs, err); } @@ -366,7 +367,7 @@ static void do_trap(int trapnr, int signr, char *str, int vm86, goto trap_signal; } - if (!(regs->xcs & 3)) + if (!user_mode(regs)) goto kernel_trap; trap_signal: { @@ -488,7 +489,7 @@ fastcall void do_general_protection(struct pt_regs * regs, long error_code) if (regs->eflags & VM_MASK) goto gp_in_vm86; - if (!(regs->xcs & 3)) + if (!user_mode(regs)) goto gp_in_kernel; current->thread.error_code = error_code; @@ -636,11 +637,13 @@ void set_nmi_callback(nmi_callback_t callback) { nmi_callback = callback; } +EXPORT_SYMBOL_GPL(set_nmi_callback); void unset_nmi_callback(void) { nmi_callback = dummy_nmi_callback; } +EXPORT_SYMBOL_GPL(unset_nmi_callback); #ifdef CONFIG_KPROBES fastcall void do_int3(struct pt_regs *regs, long error_code) @@ -682,7 +685,7 @@ fastcall void do_debug(struct pt_regs * regs, long error_code) unsigned int condition; struct task_struct *tsk = current; - __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + get_debugreg(condition, 6); if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, SIGTRAP) == NOTIFY_STOP) @@ -713,7 +716,7 @@ fastcall void do_debug(struct pt_regs * regs, long error_code) * check for kernel mode by just checking the CPL * of CS. */ - if ((regs->xcs & 3) == 0) + if (!user_mode(regs)) goto clear_TF_reenable; } @@ -724,9 +727,7 @@ fastcall void do_debug(struct pt_regs * regs, long error_code) * the signal is delivered. */ clear_dr7: - __asm__("movl %0,%%db7" - : /* no output */ - : "r" (0)); + set_debugreg(0, 7); return; debug_vm86: diff --git a/arch/i386/lib/dec_and_lock.c b/arch/i386/lib/dec_and_lock.c index ab43394dc775..8b81b2524fa6 100644 --- a/arch/i386/lib/dec_and_lock.c +++ b/arch/i386/lib/dec_and_lock.c @@ -8,6 +8,7 @@ */ #include +#include #include int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) @@ -38,3 +39,4 @@ slow_path: spin_unlock(lock); return 0; } +EXPORT_SYMBOL(_atomic_dec_and_lock); diff --git a/arch/i386/lib/delay.c b/arch/i386/lib/delay.c index eb0cdfe9280f..c49a6acbee56 100644 --- a/arch/i386/lib/delay.c +++ b/arch/i386/lib/delay.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -47,3 +48,8 @@ void __ndelay(unsigned long nsecs) { __const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */ } + +EXPORT_SYMBOL(__delay); +EXPORT_SYMBOL(__const_udelay); +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__ndelay); diff --git a/arch/i386/lib/mmx.c b/arch/i386/lib/mmx.c index 01f8b1a2cc84..2afda94dffd3 100644 --- a/arch/i386/lib/mmx.c +++ b/arch/i386/lib/mmx.c @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -397,3 +398,7 @@ void mmx_copy_page(void *to, void *from) else fast_copy_page(to, from); } + +EXPORT_SYMBOL(_mmx_memcpy); +EXPORT_SYMBOL(mmx_clear_page); +EXPORT_SYMBOL(mmx_copy_page); diff --git a/arch/i386/lib/usercopy.c b/arch/i386/lib/usercopy.c index 51aa2bbb0269..4cf981d70f45 100644 --- a/arch/i386/lib/usercopy.c +++ b/arch/i386/lib/usercopy.c @@ -84,6 +84,7 @@ __strncpy_from_user(char *dst, const char __user *src, long count) __do_strncpy_from_user(dst, src, count, res); return res; } +EXPORT_SYMBOL(__strncpy_from_user); /** * strncpy_from_user: - Copy a NUL terminated string from userspace. @@ -111,7 +112,7 @@ strncpy_from_user(char *dst, const char __user *src, long count) __do_strncpy_from_user(dst, src, count, res); return res; } - +EXPORT_SYMBOL(strncpy_from_user); /* * Zero Userspace @@ -157,6 +158,7 @@ clear_user(void __user *to, unsigned long n) __do_clear_user(to, n); return n; } +EXPORT_SYMBOL(clear_user); /** * __clear_user: - Zero a block of memory in user space, with less checking. @@ -175,6 +177,7 @@ __clear_user(void __user *to, unsigned long n) __do_clear_user(to, n); return n; } +EXPORT_SYMBOL(__clear_user); /** * strlen_user: - Get the size of a string in user space. @@ -218,6 +221,7 @@ long strnlen_user(const char __user *s, long n) :"cc"); return res & mask; } +EXPORT_SYMBOL(strnlen_user); #ifdef CONFIG_X86_INTEL_USERCOPY static unsigned long @@ -570,6 +574,7 @@ survive: n = __copy_user_intel(to, from, n); return n; } +EXPORT_SYMBOL(__copy_to_user_ll); unsigned long __copy_from_user_ll(void *to, const void __user *from, unsigned long n) @@ -581,6 +586,7 @@ __copy_from_user_ll(void *to, const void __user *from, unsigned long n) n = __copy_user_zeroing_intel(to, from, n); return n; } +EXPORT_SYMBOL(__copy_from_user_ll); /** * copy_to_user: - Copy a block of data into user space. diff --git a/arch/i386/mach-voyager/voyager_smp.c b/arch/i386/mach-voyager/voyager_smp.c index a6e0ddd65bd0..8c8527593da0 100644 --- a/arch/i386/mach-voyager/voyager_smp.c +++ b/arch/i386/mach-voyager/voyager_smp.c @@ -1288,7 +1288,7 @@ smp_local_timer_interrupt(struct pt_regs * regs) per_cpu(prof_counter, cpu); } - update_process_times(user_mode(regs)); + update_process_times(user_mode_vm(regs)); } if( ((1< #include #include +#include #include #include #include #include struct pglist_data *node_data[MAX_NUMNODES]; +EXPORT_SYMBOL(node_data); bootmem_data_t node0_bdata; /* @@ -42,12 +44,16 @@ bootmem_data_t node0_bdata; * populated the following initialisation. * * 1) node_online_map - the map of all nodes configured (online) in the system - * 2) physnode_map - the mapping between a pfn and owning node - * 3) node_start_pfn - the starting page frame number for a node + * 2) node_start_pfn - the starting page frame number for a node * 3) node_end_pfn - the ending page fram number for a node */ +unsigned long node_start_pfn[MAX_NUMNODES]; +unsigned long node_end_pfn[MAX_NUMNODES]; + +#ifdef CONFIG_DISCONTIGMEM /* + * 4) physnode_map - the mapping between a pfn and owning node * physnode_map keeps track of the physical memory layout of a generic * numa node on a 256Mb break (each element of the array will * represent 256Mb of memory and will be marked by the node id. so, @@ -59,6 +65,7 @@ bootmem_data_t node0_bdata; * physnode_map[8- ] = -1; */ s8 physnode_map[MAX_ELEMENTS] = { [0 ... (MAX_ELEMENTS - 1)] = -1}; +EXPORT_SYMBOL(physnode_map); void memory_present(int nid, unsigned long start, unsigned long end) { @@ -85,9 +92,7 @@ unsigned long node_memmap_size_bytes(int nid, unsigned long start_pfn, return (nr_pages + 1) * sizeof(struct page); } - -unsigned long node_start_pfn[MAX_NUMNODES]; -unsigned long node_end_pfn[MAX_NUMNODES]; +#endif extern unsigned long find_max_low_pfn(void); extern void find_max_pfn(void); @@ -108,6 +113,9 @@ unsigned long node_remap_offset[MAX_NUMNODES]; void *node_remap_start_vaddr[MAX_NUMNODES]; void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags); +void *node_remap_end_vaddr[MAX_NUMNODES]; +void *node_remap_alloc_vaddr[MAX_NUMNODES]; + /* * FLAT - support for basic PC memory model with discontig enabled, essentially * a single node with all available processors in it with a flat @@ -146,6 +154,21 @@ static void __init find_max_pfn_node(int nid) BUG(); } +/* Find the owning node for a pfn. */ +int early_pfn_to_nid(unsigned long pfn) +{ + int nid; + + for_each_node(nid) { + if (node_end_pfn[nid] == 0) + break; + if (node_start_pfn[nid] <= pfn && node_end_pfn[nid] >= pfn) + return nid; + } + + return 0; +} + /* * Allocate memory for the pg_data_t for this node via a crude pre-bootmem * method. For node zero take this from the bottom of memory, for @@ -163,6 +186,21 @@ static void __init allocate_pgdat(int nid) } } +void *alloc_remap(int nid, unsigned long size) +{ + void *allocation = node_remap_alloc_vaddr[nid]; + + size = ALIGN(size, L1_CACHE_BYTES); + + if (!allocation || (allocation + size) >= node_remap_end_vaddr[nid]) + return 0; + + node_remap_alloc_vaddr[nid] += size; + memset(allocation, 0, size); + + return allocation; +} + void __init remap_numa_kva(void) { void *vaddr; @@ -170,8 +208,6 @@ void __init remap_numa_kva(void) int node; for_each_online_node(node) { - if (node == 0) - continue; for (pfn=0; pfn < node_remap_size[node]; pfn += PTRS_PER_PTE) { vaddr = node_remap_start_vaddr[node]+(pfn<node_mem_map = (struct page *)lmem_map; - free_area_init_node(nid, NODE_DATA(nid), zones_size, - start, zholes_size); - } + + free_area_init_node(nid, NODE_DATA(nid), zones_size, start, + zholes_size); } return; } @@ -358,24 +395,26 @@ void __init set_highmem_pages_init(int bad_ppro) { #ifdef CONFIG_HIGHMEM struct zone *zone; + struct page *page; for_each_zone(zone) { - unsigned long node_pfn, node_high_size, zone_start_pfn; - struct page * zone_mem_map; - + unsigned long node_pfn, zone_start_pfn, zone_end_pfn; + if (!is_highmem(zone)) continue; - printk("Initializing %s for node %d\n", zone->name, - zone->zone_pgdat->node_id); - - node_high_size = zone->spanned_pages; - zone_mem_map = zone->zone_mem_map; zone_start_pfn = zone->zone_start_pfn; + zone_end_pfn = zone_start_pfn + zone->spanned_pages; - for (node_pfn = 0; node_pfn < node_high_size; node_pfn++) { - one_highpage_init((struct page *)(zone_mem_map + node_pfn), - zone_start_pfn + node_pfn, bad_ppro); + printk("Initializing %s for node %d (%08lx:%08lx)\n", + zone->name, zone->zone_pgdat->node_id, + zone_start_pfn, zone_end_pfn); + + for (node_pfn = zone_start_pfn; node_pfn < zone_end_pfn; node_pfn++) { + if (!pfn_valid(node_pfn)) + continue; + page = pfn_to_page(node_pfn); + one_highpage_init(page, node_pfn, bad_ppro); } } totalram_pages += totalhigh_pages; diff --git a/arch/i386/mm/highmem.c b/arch/i386/mm/highmem.c index fc4c4cad4e98..4b7aaf99d7ea 100644 --- a/arch/i386/mm/highmem.c +++ b/arch/i386/mm/highmem.c @@ -1,4 +1,5 @@ #include +#include void *kmap(struct page *page) { @@ -87,3 +88,8 @@ struct page *kmap_atomic_to_page(void *ptr) return pte_page(*pte); } +EXPORT_SYMBOL(kmap); +EXPORT_SYMBOL(kunmap); +EXPORT_SYMBOL(kmap_atomic); +EXPORT_SYMBOL(kunmap_atomic); +EXPORT_SYMBOL(kmap_atomic_to_page); diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 8766c771bb45..3672e2ef51ae 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -191,7 +191,7 @@ static inline int page_kills_ppro(unsigned long pagenr) extern int is_available_memory(efi_memory_desc_t *); -static inline int page_is_ram(unsigned long pagenr) +int page_is_ram(unsigned long pagenr) { int i; unsigned long addr, end; @@ -276,7 +276,9 @@ void __init one_highpage_init(struct page *page, int pfn, int bad_ppro) SetPageReserved(page); } -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA +extern void set_highmem_pages_init(int); +#else static void __init set_highmem_pages_init(int bad_ppro) { int pfn; @@ -284,9 +286,7 @@ static void __init set_highmem_pages_init(int bad_ppro) one_highpage_init(pfn_to_page(pfn), pfn, bad_ppro); totalram_pages += totalhigh_pages; } -#else -extern void set_highmem_pages_init(int); -#endif /* !CONFIG_DISCONTIGMEM */ +#endif /* CONFIG_FLATMEM */ #else #define kmap_init() do { } while (0) @@ -295,12 +295,13 @@ extern void set_highmem_pages_init(int); #endif /* CONFIG_HIGHMEM */ unsigned long long __PAGE_KERNEL = _PAGE_KERNEL; +EXPORT_SYMBOL(__PAGE_KERNEL); unsigned long long __PAGE_KERNEL_EXEC = _PAGE_KERNEL_EXEC; -#ifndef CONFIG_DISCONTIGMEM -#define remap_numa_kva() do {} while (0) -#else +#ifdef CONFIG_NUMA extern void __init remap_numa_kva(void); +#else +#define remap_numa_kva() do {} while (0) #endif static void __init pagetable_init (void) @@ -525,7 +526,7 @@ static void __init set_max_mapnr_init(void) #else num_physpages = max_low_pfn; #endif -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_FLATMEM max_mapnr = num_physpages; #endif } @@ -539,7 +540,7 @@ void __init mem_init(void) int tmp; int bad_ppro; -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_FLATMEM if (!mem_map) BUG(); #endif diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c index ab542792b27b..d393eefc7052 100644 --- a/arch/i386/mm/ioremap.c +++ b/arch/i386/mm/ioremap.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -165,7 +166,7 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l } return (void __iomem *) (offset + (char __iomem *)addr); } - +EXPORT_SYMBOL(__ioremap); /** * ioremap_nocache - map bus memory into CPU space @@ -222,6 +223,7 @@ void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) return p; } +EXPORT_SYMBOL(ioremap_nocache); void iounmap(volatile void __iomem *addr) { @@ -255,6 +257,7 @@ out_unlock: write_unlock(&vmlist_lock); kfree(p); } +EXPORT_SYMBOL(iounmap); void __init *bt_ioremap(unsigned long phys_addr, unsigned long size) { diff --git a/arch/i386/mm/pgtable.c b/arch/i386/mm/pgtable.c index dd81479ff88a..270c59f099a4 100644 --- a/arch/i386/mm/pgtable.c +++ b/arch/i386/mm/pgtable.c @@ -30,13 +30,14 @@ void show_mem(void) struct page *page; pg_data_t *pgdat; unsigned long i; + struct page_state ps; printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); for_each_pgdat(pgdat) { for (i = 0; i < pgdat->node_spanned_pages; ++i) { - page = pgdat->node_mem_map + i; + page = pgdat_page_nr(pgdat, i); total++; if (PageHighMem(page)) highmem++; @@ -53,6 +54,13 @@ void show_mem(void) printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); + + get_page_state(&ps); + printk("%lu pages dirty\n", ps.nr_dirty); + printk("%lu pages writeback\n", ps.nr_writeback); + printk("%lu pages mapped\n", ps.nr_mapped); + printk("%lu pages slab\n", ps.nr_slab); + printk("%lu pages pagetables\n", ps.nr_page_table_pages); } /* diff --git a/arch/i386/oprofile/backtrace.c b/arch/i386/oprofile/backtrace.c index 52d72e074f7f..65dfd2edb671 100644 --- a/arch/i386/oprofile/backtrace.c +++ b/arch/i386/oprofile/backtrace.c @@ -91,7 +91,7 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth) head = (struct frame_head *)regs->ebp; #endif - if (!user_mode(regs)) { + if (!user_mode_vm(regs)) { while (depth-- && valid_kernel_stack(head, regs)) head = dump_backtrace(head); return; diff --git a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c index da21b1d07c15..83458f81e661 100644 --- a/arch/i386/pci/irq.c +++ b/arch/i386/pci/irq.c @@ -226,6 +226,24 @@ static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i return 1; } +/* + * The VIA pirq rules are nibble-based, like ALI, + * but without the ugly irq number munging. + * However, for 82C586, nibble map is different . + */ +static int pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + static unsigned int pirqmap[4] = { 3, 2, 5, 1 }; + return read_config_nybble(router, 0x55, pirqmap[pirq-1]); +} + +static int pirq_via586_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + static unsigned int pirqmap[4] = { 3, 2, 5, 1 }; + write_config_nybble(router, 0x55, pirqmap[pirq-1], irq); + return 1; +} + /* * ITE 8330G pirq rules are nibble-based * FIXME: pirqmap may be { 1, 0, 3, 2 }, @@ -512,6 +530,10 @@ static __init int via_router_probe(struct irq_router *r, struct pci_dev *router, switch(device) { case PCI_DEVICE_ID_VIA_82C586_0: + r->name = "VIA"; + r->get = pirq_via586_get; + r->set = pirq_via586_set; + return 1; case PCI_DEVICE_ID_VIA_82C596: case PCI_DEVICE_ID_VIA_82C686: case PCI_DEVICE_ID_VIA_8231: diff --git a/arch/i386/pci/pcbios.c b/arch/i386/pci/pcbios.c index 141421b673b0..b9d65f0bc2d1 100644 --- a/arch/i386/pci/pcbios.c +++ b/arch/i386/pci/pcbios.c @@ -4,6 +4,7 @@ #include #include +#include #include "pci.h" #include "pci-functions.h" @@ -456,7 +457,7 @@ struct irq_routing_table * __devinit pcibios_get_irq_routing_table(void) free_page(page); return rt; } - +EXPORT_SYMBOL(pcibios_get_irq_routing_table); int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) { @@ -473,6 +474,7 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) "S" (&pci_indirect)); return !(ret & 0xff00); } +EXPORT_SYMBOL(pcibios_set_irq_routing); static int __init pci_pcbios_init(void) { diff --git a/arch/i386/power/cpu.c b/arch/i386/power/cpu.c index cf337c673d92..6f521cf19a13 100644 --- a/arch/i386/power/cpu.c +++ b/arch/i386/power/cpu.c @@ -94,13 +94,13 @@ static void fix_processor_context(void) * Now maybe reload the debug registers */ if (current->thread.debugreg[7]){ - loaddebug(¤t->thread, 0); - loaddebug(¤t->thread, 1); - loaddebug(¤t->thread, 2); - loaddebug(¤t->thread, 3); - /* no 4 and 5 */ - loaddebug(¤t->thread, 6); - loaddebug(¤t->thread, 7); + set_debugreg(current->thread.debugreg[0], 0); + set_debugreg(current->thread.debugreg[1], 1); + set_debugreg(current->thread.debugreg[2], 2); + set_debugreg(current->thread.debugreg[3], 3); + /* no 4 and 5 */ + set_debugreg(current->thread.debugreg[6], 6); + set_debugreg(current->thread.debugreg[7], 7); } } diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index ce4dfa8b834d..01b78e7f992e 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -161,6 +161,8 @@ config IA64_PAGE_SIZE_64KB endchoice +source kernel/Kconfig.hz + config IA64_BRL_EMU bool depends on ITANIUM @@ -197,7 +199,7 @@ config HOLES_IN_ZONE bool default y if VIRTUAL_MEM_MAP -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool "Discontiguous memory support" depends on (IA64_DIG || IA64_SGI_SN2 || IA64_GENERIC || IA64_HP_ZX1 || IA64_HP_ZX1_SWIOTLB) && NUMA && VIRTUAL_MEM_MAP default y if (IA64_SGI_SN2 || IA64_GENERIC) && NUMA @@ -300,6 +302,8 @@ config PREEMPT Say Y here if you are building a kernel for a desktop, embedded or real-time system. Say N if you are unsure. +source "mm/Kconfig" + config HAVE_DEC_LOCK bool depends on (SMP || PREEMPT) diff --git a/arch/ia64/Kconfig.debug b/arch/ia64/Kconfig.debug index de9d507ba0fd..fda67ac993d7 100644 --- a/arch/ia64/Kconfig.debug +++ b/arch/ia64/Kconfig.debug @@ -2,6 +2,17 @@ menu "Kernel hacking" source "lib/Kconfig.debug" +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + + choice prompt "Physical memory granularity" default IA64_GRANULE_64MB diff --git a/arch/ia64/configs/sn2_defconfig b/arch/ia64/configs/sn2_defconfig index a01bb02d074d..487d2e36b0a6 100644 --- a/arch/ia64/configs/sn2_defconfig +++ b/arch/ia64/configs/sn2_defconfig @@ -78,7 +78,7 @@ CONFIG_IA64_L1_CACHE_SHIFT=7 CONFIG_NUMA=y CONFIG_VIRTUAL_MEM_MAP=y CONFIG_HOLES_IN_ZONE=y -CONFIG_DISCONTIGMEM=y +CONFIG_ARCH_DISCONTIGMEM_ENABLE=y # CONFIG_IA64_CYCLONE is not set CONFIG_IOSAPIC=y CONFIG_IA64_SGI_SN_SIM=y diff --git a/arch/ia64/defconfig b/arch/ia64/defconfig index 7be8096e0561..8444add76380 100644 --- a/arch/ia64/defconfig +++ b/arch/ia64/defconfig @@ -84,7 +84,7 @@ CONFIG_IA64_L1_CACHE_SHIFT=7 CONFIG_NUMA=y CONFIG_VIRTUAL_MEM_MAP=y CONFIG_HOLES_IN_ZONE=y -CONFIG_DISCONTIGMEM=y +CONFIG_ARCH_DISCONTIGMEM_ENABLE=y CONFIG_IA64_CYCLONE=y CONFIG_IOSAPIC=y CONFIG_FORCE_MAX_ZONEORDER=18 diff --git a/arch/ia64/ia32/ia32priv.h b/arch/ia64/ia32/ia32priv.h index b2de948bdaea..e3e9290e3ff2 100644 --- a/arch/ia64/ia32/ia32priv.h +++ b/arch/ia64/ia32/ia32priv.h @@ -241,7 +241,7 @@ typedef struct compat_siginfo { /* POSIX.1b timers */ struct { - timer_t _tid; /* timer id */ + compat_timer_t _tid; /* timer id */ int _overrun; /* overrun count */ char _pad[sizeof(unsigned int) - sizeof(int)]; compat_sigval_t _sigval; /* same as below */ diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 4c73d8ba2e3d..b2e2f6509eb0 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o domain.o obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o obj-$(CONFIG_IA64_CYCLONE) += cyclone.o obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o +obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o mca_recovery-y += mca_drv.o mca_drv_asm.o diff --git a/arch/ia64/kernel/jprobes.S b/arch/ia64/kernel/jprobes.S new file mode 100644 index 000000000000..b7fa3ccd2b0f --- /dev/null +++ b/arch/ia64/kernel/jprobes.S @@ -0,0 +1,61 @@ +/* + * Jprobe specific 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. + * + * Copyright (C) Intel Corporation, 2005 + * + * 2005-May Rusty Lynch and Anil S Keshavamurthy + * initial implementation + * + * Jprobes (a.k.a. "jump probes" which is built on-top of kprobes) allow a + * probe to be inserted into the beginning of a function call. The fundamental + * difference between a jprobe and a kprobe is the jprobe handler is executed + * in the same context as the target function, while the kprobe handlers + * are executed in interrupt context. + * + * For jprobes we initially gain control by placing a break point in the + * first instruction of the targeted function. When we catch that specific + * break, we: + * * set the return address to our jprobe_inst_return() function + * * jump to the jprobe handler function + * + * Since we fixed up the return address, the jprobe handler will return to our + * jprobe_inst_return() function, giving us control again. At this point we + * are back in the parents frame marker, so we do yet another call to our + * jprobe_break() function to fix up the frame marker as it would normally + * exist in the target function. + * + * Our jprobe_return function then transfers control back to kprobes.c by + * executing a break instruction using one of our reserved numbers. When we + * catch that break in kprobes.c, we continue like we do for a normal kprobe + * by single stepping the emulated instruction, and then returning execution + * to the correct location. + */ +#include + + /* + * void jprobe_break(void) + */ +ENTRY(jprobe_break) + break.m 0x80300 +END(jprobe_break) + + /* + * void jprobe_inst_return(void) + */ +GLOBAL_ENTRY(jprobe_inst_return) + br.call.sptk.many b0=jprobe_break +END(jprobe_inst_return) diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c new file mode 100644 index 000000000000..5978823d5c63 --- /dev/null +++ b/arch/ia64/kernel/kprobes.c @@ -0,0 +1,601 @@ +/* + * Kernel Probes (KProbes) + * arch/ia64/kernel/kprobes.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. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * Copyright (C) Intel Corporation, 2005 + * + * 2005-Apr Rusty Lynch and Anil S Keshavamurthy + * adapted from i386 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern void jprobe_inst_return(void); + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe, *kprobe_prev; +static unsigned long kprobe_status, kprobe_status_prev; +static struct pt_regs jprobe_saved_regs; + +enum instruction_type {A, I, M, F, B, L, X, u}; +static enum instruction_type bundle_encoding[32][3] = { + { M, I, I }, /* 00 */ + { M, I, I }, /* 01 */ + { M, I, I }, /* 02 */ + { M, I, I }, /* 03 */ + { M, L, X }, /* 04 */ + { M, L, X }, /* 05 */ + { u, u, u }, /* 06 */ + { u, u, u }, /* 07 */ + { M, M, I }, /* 08 */ + { M, M, I }, /* 09 */ + { M, M, I }, /* 0A */ + { M, M, I }, /* 0B */ + { M, F, I }, /* 0C */ + { M, F, I }, /* 0D */ + { M, M, F }, /* 0E */ + { M, M, F }, /* 0F */ + { M, I, B }, /* 10 */ + { M, I, B }, /* 11 */ + { M, B, B }, /* 12 */ + { M, B, B }, /* 13 */ + { u, u, u }, /* 14 */ + { u, u, u }, /* 15 */ + { B, B, B }, /* 16 */ + { B, B, B }, /* 17 */ + { M, M, B }, /* 18 */ + { M, M, B }, /* 19 */ + { u, u, u }, /* 1A */ + { u, u, u }, /* 1B */ + { M, F, B }, /* 1C */ + { M, F, B }, /* 1D */ + { u, u, u }, /* 1E */ + { u, u, u }, /* 1F */ +}; + +/* + * In this function we check to see if the instruction + * is IP relative instruction and update the kprobe + * inst flag accordingly + */ +static void update_kprobe_inst_flag(uint template, uint slot, uint major_opcode, + unsigned long kprobe_inst, struct kprobe *p) +{ + p->ainsn.inst_flag = 0; + p->ainsn.target_br_reg = 0; + + if (bundle_encoding[template][slot] == B) { + switch (major_opcode) { + case INDIRECT_CALL_OPCODE: + p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; + p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); + break; + case IP_RELATIVE_PREDICT_OPCODE: + case IP_RELATIVE_BRANCH_OPCODE: + p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR; + break; + case IP_RELATIVE_CALL_OPCODE: + p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR; + p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; + p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); + break; + } + } else if (bundle_encoding[template][slot] == X) { + switch (major_opcode) { + case LONG_CALL_OPCODE: + p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG; + p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7); + break; + } + } + return; +} + +/* + * In this function we check to see if the instruction + * on which we are inserting kprobe is supported. + * Returns 0 if supported + * Returns -EINVAL if unsupported + */ +static int unsupported_inst(uint template, uint slot, uint major_opcode, + unsigned long kprobe_inst, struct kprobe *p) +{ + unsigned long addr = (unsigned long)p->addr; + + if (bundle_encoding[template][slot] == I) { + switch (major_opcode) { + case 0x0: //I_UNIT_MISC_OPCODE: + /* + * Check for Integer speculation instruction + * - Bit 33-35 to be equal to 0x1 + */ + if (((kprobe_inst >> 33) & 0x7) == 1) { + printk(KERN_WARNING + "Kprobes on speculation inst at <0x%lx> not supported\n", + addr); + return -EINVAL; + } + + /* + * IP relative mov instruction + * - Bit 27-35 to be equal to 0x30 + */ + if (((kprobe_inst >> 27) & 0x1FF) == 0x30) { + printk(KERN_WARNING + "Kprobes on \"mov r1=ip\" at <0x%lx> not supported\n", + addr); + return -EINVAL; + + } + } + } + return 0; +} + + +/* + * In this function we check to see if the instruction + * (qp) cmpx.crel.ctype p1,p2=r2,r3 + * on which we are inserting kprobe is cmp instruction + * with ctype as unc. + */ +static uint is_cmp_ctype_unc_inst(uint template, uint slot, uint major_opcode, +unsigned long kprobe_inst) +{ + cmp_inst_t cmp_inst; + uint ctype_unc = 0; + + if (!((bundle_encoding[template][slot] == I) || + (bundle_encoding[template][slot] == M))) + goto out; + + if (!((major_opcode == 0xC) || (major_opcode == 0xD) || + (major_opcode == 0xE))) + goto out; + + cmp_inst.l = kprobe_inst; + if ((cmp_inst.f.x2 == 0) || (cmp_inst.f.x2 == 1)) { + /* Integere compare - Register Register (A6 type)*/ + if ((cmp_inst.f.tb == 0) && (cmp_inst.f.ta == 0) + &&(cmp_inst.f.c == 1)) + ctype_unc = 1; + } else if ((cmp_inst.f.x2 == 2)||(cmp_inst.f.x2 == 3)) { + /* Integere compare - Immediate Register (A8 type)*/ + if ((cmp_inst.f.ta == 0) &&(cmp_inst.f.c == 1)) + ctype_unc = 1; + } +out: + return ctype_unc; +} + +/* + * In this function we override the bundle with + * the break instruction at the given slot. + */ +static void prepare_break_inst(uint template, uint slot, uint major_opcode, + unsigned long kprobe_inst, struct kprobe *p) +{ + unsigned long break_inst = BREAK_INST; + bundle_t *bundle = &p->ainsn.insn.bundle; + + /* + * Copy the original kprobe_inst qualifying predicate(qp) + * to the break instruction iff !is_cmp_ctype_unc_inst + * because for cmp instruction with ctype equal to unc, + * which is a special instruction always needs to be + * executed regradless of qp + */ + if (!is_cmp_ctype_unc_inst(template, slot, major_opcode, kprobe_inst)) + break_inst |= (0x3f & kprobe_inst); + + switch (slot) { + case 0: + bundle->quad0.slot0 = break_inst; + break; + case 1: + bundle->quad0.slot1_p0 = break_inst; + bundle->quad1.slot1_p1 = break_inst >> (64-46); + break; + case 2: + bundle->quad1.slot2 = break_inst; + break; + } + + /* + * Update the instruction flag, so that we can + * emulate the instruction properly after we + * single step on original instruction + */ + update_kprobe_inst_flag(template, slot, major_opcode, kprobe_inst, p); +} + +static inline void get_kprobe_inst(bundle_t *bundle, uint slot, + unsigned long *kprobe_inst, uint *major_opcode) +{ + unsigned long kprobe_inst_p0, kprobe_inst_p1; + unsigned int template; + + template = bundle->quad0.template; + + switch (slot) { + case 0: + *major_opcode = (bundle->quad0.slot0 >> SLOT0_OPCODE_SHIFT); + *kprobe_inst = bundle->quad0.slot0; + break; + case 1: + *major_opcode = (bundle->quad1.slot1_p1 >> SLOT1_p1_OPCODE_SHIFT); + kprobe_inst_p0 = bundle->quad0.slot1_p0; + kprobe_inst_p1 = bundle->quad1.slot1_p1; + *kprobe_inst = kprobe_inst_p0 | (kprobe_inst_p1 << (64-46)); + break; + case 2: + *major_opcode = (bundle->quad1.slot2 >> SLOT2_OPCODE_SHIFT); + *kprobe_inst = bundle->quad1.slot2; + break; + } +} + +static int valid_kprobe_addr(int template, int slot, unsigned long addr) +{ + if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) { + printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n", + addr); + return -EINVAL; + } + return 0; +} + +static inline void save_previous_kprobe(void) +{ + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; +} + +static inline void restore_previous_kprobe(void) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; +} + +static inline void set_current_kprobe(struct kprobe *p) +{ + current_kprobe = p; +} + +int arch_prepare_kprobe(struct kprobe *p) +{ + unsigned long addr = (unsigned long) p->addr; + unsigned long *kprobe_addr = (unsigned long *)(addr & ~0xFULL); + unsigned long kprobe_inst=0; + unsigned int slot = addr & 0xf, template, major_opcode = 0; + bundle_t *bundle = &p->ainsn.insn.bundle; + + memcpy(&p->opcode.bundle, kprobe_addr, sizeof(bundle_t)); + memcpy(&p->ainsn.insn.bundle, kprobe_addr, sizeof(bundle_t)); + + template = bundle->quad0.template; + + if(valid_kprobe_addr(template, slot, addr)) + return -EINVAL; + + /* Move to slot 2, if bundle is MLX type and kprobe slot is 1 */ + if (slot == 1 && bundle_encoding[template][1] == L) + slot++; + + /* Get kprobe_inst and major_opcode from the bundle */ + get_kprobe_inst(bundle, slot, &kprobe_inst, &major_opcode); + + if (unsupported_inst(template, slot, major_opcode, kprobe_inst, p)) + return -EINVAL; + + prepare_break_inst(template, slot, major_opcode, kprobe_inst, p); + + return 0; +} + +void arch_arm_kprobe(struct kprobe *p) +{ + unsigned long addr = (unsigned long)p->addr; + unsigned long arm_addr = addr & ~0xFULL; + + memcpy((char *)arm_addr, &p->ainsn.insn.bundle, sizeof(bundle_t)); + flush_icache_range(arm_addr, arm_addr + sizeof(bundle_t)); +} + +void arch_disarm_kprobe(struct kprobe *p) +{ + unsigned long addr = (unsigned long)p->addr; + unsigned long arm_addr = addr & ~0xFULL; + + /* p->opcode contains the original unaltered bundle */ + memcpy((char *) arm_addr, (char *) &p->opcode.bundle, sizeof(bundle_t)); + flush_icache_range(arm_addr, arm_addr + sizeof(bundle_t)); +} + +void arch_remove_kprobe(struct kprobe *p) +{ +} + +/* + * We are resuming execution after a single step fault, so the pt_regs + * structure reflects the register state after we executed the instruction + * located in the kprobe (p->ainsn.insn.bundle). We still need to adjust + * the ip to point back to the original stack address. To set the IP address + * to original stack address, handle the case where we need to fixup the + * relative IP address and/or fixup branch register. + */ +static void resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long bundle_addr = ((unsigned long) (&p->opcode.bundle)) & ~0xFULL; + unsigned long resume_addr = (unsigned long)p->addr & ~0xFULL; + unsigned long template; + int slot = ((unsigned long)p->addr & 0xf); + + template = p->opcode.bundle.quad0.template; + + if (slot == 1 && bundle_encoding[template][1] == L) + slot = 2; + + if (p->ainsn.inst_flag) { + + if (p->ainsn.inst_flag & INST_FLAG_FIX_RELATIVE_IP_ADDR) { + /* Fix relative IP address */ + regs->cr_iip = (regs->cr_iip - bundle_addr) + resume_addr; + } + + if (p->ainsn.inst_flag & INST_FLAG_FIX_BRANCH_REG) { + /* + * Fix target branch register, software convention is + * to use either b0 or b6 or b7, so just checking + * only those registers + */ + switch (p->ainsn.target_br_reg) { + case 0: + if ((regs->b0 == bundle_addr) || + (regs->b0 == bundle_addr + 0x10)) { + regs->b0 = (regs->b0 - bundle_addr) + + resume_addr; + } + break; + case 6: + if ((regs->b6 == bundle_addr) || + (regs->b6 == bundle_addr + 0x10)) { + regs->b6 = (regs->b6 - bundle_addr) + + resume_addr; + } + break; + case 7: + if ((regs->b7 == bundle_addr) || + (regs->b7 == bundle_addr + 0x10)) { + regs->b7 = (regs->b7 - bundle_addr) + + resume_addr; + } + break; + } /* end switch */ + } + goto turn_ss_off; + } + + if (slot == 2) { + if (regs->cr_iip == bundle_addr + 0x10) { + regs->cr_iip = resume_addr + 0x10; + } + } else { + if (regs->cr_iip == bundle_addr) { + regs->cr_iip = resume_addr; + } + } + +turn_ss_off: + /* Turn off Single Step bit */ + ia64_psr(regs)->ss = 0; +} + +static void prepare_ss(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long bundle_addr = (unsigned long) &p->opcode.bundle; + unsigned long slot = (unsigned long)p->addr & 0xf; + + /* Update instruction pointer (IIP) and slot number (IPSR.ri) */ + regs->cr_iip = bundle_addr & ~0xFULL; + + if (slot > 2) + slot = 0; + + ia64_psr(regs)->ri = slot; + + /* turn on single stepping */ + ia64_psr(regs)->ss = 1; +} + +static int pre_kprobes_handler(struct die_args *args) +{ + struct kprobe *p; + int ret = 0; + struct pt_regs *regs = args->regs; + kprobe_opcode_t *addr = (kprobe_opcode_t *)instruction_pointer(regs); + + preempt_disable(); + + /* Handle recursion cases */ + if (kprobe_running()) { + p = get_kprobe(addr); + if (p) { + if (kprobe_status == KPROBE_HIT_SS) { + unlock_kprobes(); + goto no_kprobe; + } + /* We have reentered the pre_kprobe_handler(), since + * another probe was hit while within the handler. + * We here save the original kprobes variables and + * just single step on the instruction of the new probe + * without calling any user handlers. + */ + save_previous_kprobe(); + set_current_kprobe(p); + p->nmissed++; + prepare_ss(p, regs); + kprobe_status = KPROBE_REENTER; + return 1; + } else if (args->err == __IA64_BREAK_JPROBE) { + /* + * jprobe instrumented function just completed + */ + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) { + goto ss_probe; + } + } else { + /* Not our break */ + goto no_kprobe; + } + } + + lock_kprobes(); + p = get_kprobe(addr); + if (!p) { + unlock_kprobes(); + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + set_current_kprobe(p); + + if (p->pre_handler && p->pre_handler(p, regs)) + /* + * Our pre-handler is specifically requesting that we just + * do a return. This is handling the case where the + * pre-handler is really our special jprobe pre-handler. + */ + return 1; + +ss_probe: + prepare_ss(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +static int post_kprobes_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { + kprobe_status = KPROBE_HIT_SSDONE; + current_kprobe->post_handler(current_kprobe, regs, 0); + } + + resume_execution(current_kprobe, regs); + + /*Restore back the original saved kprobes variables and continue. */ + if (kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(); + goto out; + } + + unlock_kprobes(); + +out: + preempt_enable_no_resched(); + return 1; +} + +static int kprobes_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->fault_handler && + current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(current_kprobe, regs); + unlock_kprobes(); + preempt_enable_no_resched(); + } + + return 0; +} + +int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = (struct die_args *)data; + switch(val) { + case DIE_BREAK: + if (pre_kprobes_handler(args)) + return NOTIFY_STOP; + break; + case DIE_SS: + if (post_kprobes_handler(args->regs)) + return NOTIFY_STOP; + break; + case DIE_PAGE_FAULT: + if (kprobes_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + default: + break; + } + return NOTIFY_DONE; +} + +int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + unsigned long addr = ((struct fnptr *)(jp->entry))->ip; + + /* save architectural state */ + jprobe_saved_regs = *regs; + + /* after rfi, execute the jprobe instrumented function */ + regs->cr_iip = addr & ~0xFULL; + ia64_psr(regs)->ri = addr & 0xf; + regs->r1 = ((struct fnptr *)(jp->entry))->gp; + + /* + * fix the return address to our jprobe_inst_return() function + * in the jprobes.S file + */ + regs->b0 = ((struct fnptr *)(jprobe_inst_return))->ip; + + return 1; +} + +int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + *regs = jprobe_saved_regs; + return 1; +} diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index 1861173bd4f6..e7e520d90f03 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -21,12 +21,26 @@ #include #include #include +#include extern spinlock_t timerlist_lock; fpswa_interface_t *fpswa_interface; EXPORT_SYMBOL(fpswa_interface); +struct notifier_block *ia64die_chain; +static DEFINE_SPINLOCK(die_notifier_lock); + +int register_die_notifier(struct notifier_block *nb) +{ + int err = 0; + unsigned long flags; + spin_lock_irqsave(&die_notifier_lock, flags); + err = notifier_chain_register(&ia64die_chain, nb); + spin_unlock_irqrestore(&die_notifier_lock, flags); + return err; +} + void __init trap_init (void) { @@ -137,6 +151,10 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs) switch (break_num) { case 0: /* unknown error (used by GCC for __builtin_abort()) */ + if (notify_die(DIE_BREAK, "break 0", regs, break_num, TRAP_BRKPT, SIGTRAP) + == NOTIFY_STOP) { + return; + } die_if_kernel("bugcheck!", regs, break_num); sig = SIGILL; code = ILL_ILLOPC; break; @@ -189,6 +207,15 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs) sig = SIGILL; code = __ILL_BNDMOD; break; + case 0x80200: + case 0x80300: + if (notify_die(DIE_BREAK, "kprobe", regs, break_num, TRAP_BRKPT, SIGTRAP) + == NOTIFY_STOP) { + return; + } + sig = SIGTRAP; code = TRAP_BRKPT; + break; + default: if (break_num < 0x40000 || break_num > 0x100000) die_if_kernel("Bad break", regs, break_num); @@ -548,7 +575,11 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, #endif break; case 35: siginfo.si_code = TRAP_BRANCH; ifa = 0; break; - case 36: siginfo.si_code = TRAP_TRACE; ifa = 0; break; + case 36: + if (notify_die(DIE_SS, "ss", ®s, vector, + vector, SIGTRAP) == NOTIFY_STOP) + return; + siginfo.si_code = TRAP_TRACE; ifa = 0; break; } siginfo.si_signo = SIGTRAP; siginfo.si_errno = 0; diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index c00710929390..f3fd528ead3b 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -560,14 +560,15 @@ void show_mem(void) int shared = 0, cached = 0, reserved = 0; printk("Node ID: %d\n", pgdat->node_id); for(i = 0; i < pgdat->node_spanned_pages; i++) { + struct page *page = pgdat_page_nr(pgdat, i); if (!ia64_pfn_valid(pgdat->node_start_pfn+i)) continue; - if (PageReserved(pgdat->node_mem_map+i)) + if (PageReserved(page)) reserved++; - else if (PageSwapCache(pgdat->node_mem_map+i)) + else if (PageSwapCache(page)) cached++; - else if (page_count(pgdat->node_mem_map+i)) - shared += page_count(pgdat->node_mem_map+i)-1; + else if (page_count(page)) + shared += page_count(page)-1; } total_present += present; total_reserved += reserved; diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c index 4174ec999dde..ff62551eb3a1 100644 --- a/arch/ia64/mm/fault.c +++ b/arch/ia64/mm/fault.c @@ -14,6 +14,7 @@ #include #include #include +#include extern void die (char *, struct pt_regs *, long); @@ -102,6 +103,13 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re goto bad_area_no_up; #endif + /* + * This is to handle the kprobes on user space access instructions + */ + if (notify_die(DIE_PAGE_FAULT, "page fault", regs, code, TRAP_BRKPT, + SIGSEGV) == NOTIFY_STOP) + return; + down_read(&mm->mmap_sem); vma = find_vma_prev(mm, address, &prev_vma); diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index 64c133344afe..42ca8a39798d 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -172,11 +172,13 @@ config NOHIGHMEM bool default y -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool "Internal RAM Support" depends on CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP default y +source "mm/Kconfig" + config IRAM_START hex "Internal memory start address (hex)" default "00f00000" diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index bc423d838fb8..d9a40b1fe8ba 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -49,7 +49,7 @@ void show_mem(void) printk("Free swap: %6ldkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); for_each_pgdat(pgdat) { for (i = 0; i < pgdat->node_spanned_pages; ++i) { - page = pgdat->node_mem_map + i; + page = pgdat_page_nr(pgdat, i); total++; if (PageHighMem(page)) highmem++; @@ -152,7 +152,7 @@ int __init reservedpages_count(void) reservedpages = 0; for_each_online_node(nid) for (i = 0 ; i < MAX_LOW_PFN(nid) - START_PFN(nid) ; i++) - if (PageReserved(NODE_DATA(nid)->node_mem_map + i)) + if (PageReserved(nid_page_nr(nid, i))) reservedpages++; return reservedpages; diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index d0713c7d9f0a..691a2469ff36 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -357,6 +357,8 @@ config 060_WRITETHROUGH is hardwired on. The 53c710 SCSI driver is known to suffer from this problem. +source "mm/Kconfig" + endmenu menu "General setup" diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index e729bd280623..dbfcdc8e6087 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -532,6 +532,8 @@ config ROMKERNEL endchoice +source "mm/Kconfig" + endmenu config ISA_DMA_API diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index ab9944693f1f..94f5a8eb2c22 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -492,7 +492,7 @@ config SGI_SN0_N_MODE which allows for more memory. Your system is most probably running in M-Mode, so you should say N here. -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool default y if SGI_IP27 help diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig index 13472292d0ec..b5bab3a42fc4 100644 --- a/arch/mips/configs/ip27_defconfig +++ b/arch/mips/configs/ip27_defconfig @@ -82,7 +82,7 @@ CONFIG_STOP_MACHINE=y # CONFIG_SGI_IP22 is not set CONFIG_SGI_IP27=y # CONFIG_SGI_SN0_N_MODE is not set -CONFIG_DISCONTIGMEM=y +CONFIG_ARCH_DISCONTIGMEM_ENABLE=y CONFIG_NUMA=y # CONFIG_MAPPED_KERNEL is not set # CONFIG_REPLICATE_KTEXT is not set diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index 0a44a98d7adc..a160d04f7dbe 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -549,9 +549,8 @@ void __init mem_init(void) */ numslots = node_getlastslot(node); for (slot = 1; slot <= numslots; slot++) { - p = NODE_DATA(node)->node_mem_map + - (slot_getbasepfn(node, slot) - - slot_getbasepfn(node, 0)); + p = nid_page_nr(node, slot_getbasepfn(node, slot) - + slot_getbasepfn(node, 0)); /* * Free valid memory in current slot. diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index e7e7c56fc212..ce327c799b44 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -148,7 +148,7 @@ config HOTPLUG_CPU default y if SMP select HOTPLUG -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool "Discontiguous memory support (EXPERIMENTAL)" depends on EXPERIMENTAL help @@ -157,6 +157,8 @@ config DISCONTIGMEM or have huge holes in the physical address space for other reasons. See for more. +source "mm/Kconfig" + config PREEMPT bool # bool "Preemptible Kernel" diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index cac37589e35c..2886ad70db48 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -506,7 +506,7 @@ void show_mem(void) for (j = node_start_pfn(i); j < node_end_pfn(i); j++) { struct page *p; - p = node_mem_map(i) + j - node_start_pfn(i); + p = nid_page_nr(i, j) - node_start_pfn(i); total++; if (PageReserved(p)) diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index 10162b187bcf..848f43970a4b 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -905,6 +905,8 @@ config PREEMPT config HIGHMEM bool "High memory support" +source "mm/Kconfig" + source "fs/Kconfig.binfmt" config PROC_DEVICETREE diff --git a/arch/ppc/boot/simple/misc.c b/arch/ppc/boot/simple/misc.c index ab0f9902cb67..e02de5b467a4 100644 --- a/arch/ppc/boot/simple/misc.c +++ b/arch/ppc/boot/simple/misc.c @@ -222,7 +222,7 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) puts("\n"); puts("Uncompressing Linux..."); - gunzip(0x0, 0x400000, zimage_start, &zimage_size); + gunzip(NULL, 0x400000, zimage_start, &zimage_size); puts("done.\n"); /* get the bi_rec address */ diff --git a/arch/ppc/boot/simple/mpc10x_memory.c b/arch/ppc/boot/simple/mpc10x_memory.c index 977daedc14c0..20d92a34ceb8 100644 --- a/arch/ppc/boot/simple/mpc10x_memory.c +++ b/arch/ppc/boot/simple/mpc10x_memory.c @@ -33,7 +33,7 @@ #define MPC10X_PCI_OP(rw, size, type, op, mask) \ static void \ -mpc10x_##rw##_config_##size(unsigned int *cfg_addr, \ +mpc10x_##rw##_config_##size(unsigned int __iomem *cfg_addr, \ unsigned int *cfg_data, int devfn, int offset, \ type val) \ { \ diff --git a/arch/ppc64/Kconfig b/arch/ppc64/Kconfig index 0f1fa289744e..cb27068bfcd4 100644 --- a/arch/ppc64/Kconfig +++ b/arch/ppc64/Kconfig @@ -77,6 +77,10 @@ config PPC_PSERIES bool " IBM pSeries & new iSeries" default y +config PPC_BPA + bool " Broadband Processor Architecture" + depends on PPC_MULTIPLATFORM + config PPC_PMAC depends on PPC_MULTIPLATFORM bool " Apple G5 based machines" @@ -106,6 +110,21 @@ config PPC_OF bool default y +config XICS + depends on PPC_PSERIES + bool + default y + +config MPIC + depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE + bool + default y + +config BPA_IIC + depends on PPC_BPA + bool + default y + # VMX is pSeries only for now until somebody writes the iSeries # exception vectors for it config ALTIVEC @@ -198,13 +217,49 @@ config HMT This option enables hardware multithreading on RS64 cpus. pSeries systems p620 and p660 have such a cpu type. -config DISCONTIGMEM - bool "Discontiguous Memory Support" +config ARCH_SELECT_MEMORY_MODEL + def_bool y + +config ARCH_FLATMEM_ENABLE + def_bool y + depends on !NUMA + +config ARCH_DISCONTIGMEM_ENABLE + def_bool y depends on SMP && PPC_PSERIES +config ARCH_DISCONTIGMEM_DEFAULT + def_bool y + depends on ARCH_DISCONTIGMEM_ENABLE + +config ARCH_FLATMEM_ENABLE + def_bool y + +config ARCH_SPARSEMEM_ENABLE + def_bool y + depends on ARCH_DISCONTIGMEM_ENABLE + +source "mm/Kconfig" + +config HAVE_ARCH_EARLY_PFN_TO_NID + def_bool y + depends on NEED_MULTIPLE_NODES + +# Some NUMA nodes have memory ranges that span +# other nodes. Even though a pfn is valid and +# between a node's start and end pfns, it may not +# reside on that node. +# +# This is a relatively temporary hack that should +# be able to go away when sparsemem is fully in +# place +config NODES_SPAN_OTHER_NODES + def_bool y + depends on NEED_MULTIPLE_NODES + config NUMA bool "NUMA support" - depends on DISCONTIGMEM + default y if DISCONTIGMEM || SPARSEMEM config SCHED_SMT bool "SMT (Hyperthreading) scheduler support" @@ -256,7 +311,7 @@ config MSCHUNKS config PPC_RTAS bool - depends on PPC_PSERIES + depends on PPC_PSERIES || PPC_BPA default y config RTAS_PROC diff --git a/arch/ppc64/Makefile b/arch/ppc64/Makefile index 33c752ceca4b..731b84758331 100644 --- a/arch/ppc64/Makefile +++ b/arch/ppc64/Makefile @@ -90,12 +90,14 @@ boot := arch/ppc64/boot boottarget-$(CONFIG_PPC_PSERIES) := zImage zImage.initrd boottarget-$(CONFIG_PPC_MAPLE) := zImage zImage.initrd boottarget-$(CONFIG_PPC_ISERIES) := vmlinux.sminitrd vmlinux.initrd vmlinux.sm +boottarget-$(CONFIG_PPC_BPA) := zImage zImage.initrd $(boottarget-y): vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ bootimage-$(CONFIG_PPC_PSERIES) := $(boot)/zImage bootimage-$(CONFIG_PPC_PMAC) := vmlinux bootimage-$(CONFIG_PPC_MAPLE) := $(boot)/zImage +bootimage-$(CONFIG_PPC_BPA) := zImage bootimage-$(CONFIG_PPC_ISERIES) := vmlinux BOOTIMAGE := $(bootimage-y) install: vmlinux diff --git a/arch/ppc64/boot/install.sh b/arch/ppc64/boot/install.sh index 955c5681db6c..cb2d6626b555 100644 --- a/arch/ppc64/boot/install.sh +++ b/arch/ppc64/boot/install.sh @@ -22,8 +22,8 @@ # 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 +if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi +if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then exec /sbin/${CROSS_COMPILE}installkernel "$@"; fi # Default install diff --git a/arch/ppc64/configs/pSeries_defconfig b/arch/ppc64/configs/pSeries_defconfig index 3eb5ef25d3a3..d0db8b5966c0 100644 --- a/arch/ppc64/configs/pSeries_defconfig +++ b/arch/ppc64/configs/pSeries_defconfig @@ -88,7 +88,7 @@ CONFIG_IBMVIO=y CONFIG_IOMMU_VMERGE=y CONFIG_SMP=y CONFIG_NR_CPUS=128 -CONFIG_DISCONTIGMEM=y +CONFIG_ARCH_DISCONTIGMEM_ENABLE=y CONFIG_NUMA=y CONFIG_SCHED_SMT=y # CONFIG_PREEMPT is not set diff --git a/arch/ppc64/defconfig b/arch/ppc64/defconfig index 2f31bf3046f9..b8e2066dde77 100644 --- a/arch/ppc64/defconfig +++ b/arch/ppc64/defconfig @@ -89,7 +89,7 @@ CONFIG_BOOTX_TEXT=y CONFIG_IOMMU_VMERGE=y CONFIG_SMP=y CONFIG_NR_CPUS=32 -CONFIG_DISCONTIGMEM=y +CONFIG_ARCH_DISCONTIGMEM_ENABLE=y # CONFIG_NUMA is not set # CONFIG_SCHED_SMT is not set # CONFIG_PREEMPT is not set diff --git a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile index b5e167cf1a05..dffbfb7ac8d5 100644 --- a/arch/ppc64/kernel/Makefile +++ b/arch/ppc64/kernel/Makefile @@ -27,17 +27,21 @@ obj-$(CONFIG_PPC_ISERIES) += HvCall.o HvLpConfig.o LparData.o \ mf.o HvLpEvent.o iSeries_proc.o iSeries_htab.o \ iSeries_iommu.o -obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o mpic.o +obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \ pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \ - xics.o rtas.o pSeries_setup.o pSeries_iommu.o + pSeries_setup.o pSeries_iommu.o + +obj-$(CONFIG_PPC_BPA) += bpa_setup.o bpa_iommu.o bpa_nvram.o \ + bpa_iic.o spider-pic.o obj-$(CONFIG_EEH) += eeh.o obj-$(CONFIG_PROC_FS) += proc_ppc64.o obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o +obj-$(CONFIG_PPC_RTAS) += rtas.o rtas_pci.o obj-$(CONFIG_RTAS_PROC) += rtas-proc.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_VIOPATH) += viopath.o @@ -46,6 +50,8 @@ obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_BOOTX_TEXT) += btext.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_IBMVIO) += vio.o +obj-$(CONFIG_XICS) += xics.o +obj-$(CONFIG_MPIC) += mpic.o obj-$(CONFIG_PPC_PMAC) += pmac_setup.o pmac_feature.o pmac_pci.o \ pmac_time.o pmac_nvram.o pmac_low_i2c.o @@ -58,6 +64,7 @@ ifdef CONFIG_SMP obj-$(CONFIG_PPC_PMAC) += pmac_smp.o smp-tbsync.o obj-$(CONFIG_PPC_ISERIES) += iSeries_smp.o obj-$(CONFIG_PPC_PSERIES) += pSeries_smp.o +obj-$(CONFIG_PPC_BPA) += pSeries_smp.o obj-$(CONFIG_PPC_MAPLE) += smp-tbsync.o endif diff --git a/arch/ppc64/kernel/bpa_iic.c b/arch/ppc64/kernel/bpa_iic.c new file mode 100644 index 000000000000..c8f3dc3fad70 --- /dev/null +++ b/arch/ppc64/kernel/bpa_iic.c @@ -0,0 +1,270 @@ +/* + * BPA Internal Interrupt Controller + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "bpa_iic.h" + +struct iic_pending_bits { + u32 data; + u8 flags; + u8 class; + u8 source; + u8 prio; +}; + +enum iic_pending_flags { + IIC_VALID = 0x80, + IIC_IPI = 0x40, +}; + +struct iic_regs { + struct iic_pending_bits pending; + struct iic_pending_bits pending_destr; + u64 generate; + u64 prio; +}; + +struct iic { + struct iic_regs __iomem *regs; +}; + +static DEFINE_PER_CPU(struct iic, iic); + +void iic_local_enable(void) +{ + out_be64(&__get_cpu_var(iic).regs->prio, 0xff); +} + +void iic_local_disable(void) +{ + out_be64(&__get_cpu_var(iic).regs->prio, 0x0); +} + +static unsigned int iic_startup(unsigned int irq) +{ + return 0; +} + +static void iic_enable(unsigned int irq) +{ + iic_local_enable(); +} + +static void iic_disable(unsigned int irq) +{ +} + +static void iic_end(unsigned int irq) +{ + iic_local_enable(); +} + +static struct hw_interrupt_type iic_pic = { + .typename = " BPA-IIC ", + .startup = iic_startup, + .enable = iic_enable, + .disable = iic_disable, + .end = iic_end, +}; + +static int iic_external_get_irq(struct iic_pending_bits pending) +{ + int irq; + unsigned char node, unit; + + node = pending.source >> 4; + unit = pending.source & 0xf; + irq = -1; + + /* + * This mapping is specific to the Broadband + * Engine. We might need to get the numbers + * from the device tree to support future CPUs. + */ + switch (unit) { + case 0x00: + case 0x0b: + /* + * One of these units can be connected + * to an external interrupt controller. + */ + if (pending.prio > 0x3f || + pending.class != 2) + break; + irq = IIC_EXT_OFFSET + + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE) + + node * IIC_NODE_STRIDE; + break; + case 0x01 ... 0x04: + case 0x07 ... 0x0a: + /* + * These units are connected to the SPEs + */ + if (pending.class > 2) + break; + irq = IIC_SPE_OFFSET + + pending.class * IIC_CLASS_STRIDE + + node * IIC_NODE_STRIDE + + unit; + break; + } + if (irq == -1) + printk(KERN_WARNING "Unexpected interrupt class %02x, " + "source %02x, prio %02x, cpu %02x\n", pending.class, + pending.source, pending.prio, smp_processor_id()); + return irq; +} + +/* Get an IRQ number from the pending state register of the IIC */ +int iic_get_irq(struct pt_regs *regs) +{ + struct iic *iic; + int irq; + struct iic_pending_bits pending; + + iic = &__get_cpu_var(iic); + *(unsigned long *) &pending = + in_be64((unsigned long __iomem *) &iic->regs->pending_destr); + + irq = -1; + if (pending.flags & IIC_VALID) { + if (pending.flags & IIC_IPI) { + irq = IIC_IPI_OFFSET + (pending.prio >> 4); +/* + if (irq > 0x80) + printk(KERN_WARNING "Unexpected IPI prio %02x" + "on CPU %02x\n", pending.prio, + smp_processor_id()); +*/ + } else { + irq = iic_external_get_irq(pending); + } + } + return irq; +} + +static struct iic_regs __iomem *find_iic(int cpu) +{ + struct device_node *np; + int nodeid = cpu / 2; + unsigned long regs; + struct iic_regs __iomem *iic_regs; + + for (np = of_find_node_by_type(NULL, "cpu"); + np; + np = of_find_node_by_type(np, "cpu")) { + if (nodeid == *(int *)get_property(np, "node-id", NULL)) + break; + } + + if (!np) { + printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); + iic_regs = NULL; + } else { + regs = *(long *)get_property(np, "iic", NULL); + + /* hack until we have decided on the devtree info */ + regs += 0x400; + if (cpu & 1) + regs += 0x20; + + printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs); + iic_regs = __ioremap(regs, sizeof(struct iic_regs), + _PAGE_NO_CACHE); + } + return iic_regs; +} + +#ifdef CONFIG_SMP +void iic_setup_cpu(void) +{ + out_be64(&__get_cpu_var(iic).regs->prio, 0xff); +} + +void iic_cause_IPI(int cpu, int mesg) +{ + out_be64(&per_cpu(iic, cpu).regs->generate, mesg); +} + +static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) +{ + + smp_message_recv(irq - IIC_IPI_OFFSET, regs); + return IRQ_HANDLED; +} + +static void iic_request_ipi(int irq, const char *name) +{ + /* IPIs are marked SA_INTERRUPT as they must run with irqs + * disabled */ + get_irq_desc(irq)->handler = &iic_pic; + get_irq_desc(irq)->status |= IRQ_PER_CPU; + request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL); +} + +void iic_request_IPIs(void) +{ + iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_CALL_FUNCTION, "IPI-call"); + iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_RESCHEDULE, "IPI-resched"); +#ifdef CONFIG_DEBUGGER + iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); +#endif /* CONFIG_DEBUGGER */ +} +#endif /* CONFIG_SMP */ + +static void iic_setup_spe_handlers(void) +{ + int be, isrc; + + /* Assume two threads per BE are present */ + for (be=0; be < num_present_cpus() / 2; be++) { + for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { + int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; + get_irq_desc(irq)->handler = &iic_pic; + } + } +} + +void iic_init_IRQ(void) +{ + int cpu, irq_offset; + struct iic *iic; + + irq_offset = 0; + for_each_cpu(cpu) { + iic = &per_cpu(iic, cpu); + iic->regs = find_iic(cpu); + if (iic->regs) + out_be64(&iic->regs->prio, 0xff); + } + iic_setup_spe_handlers(); +} diff --git a/arch/ppc64/kernel/bpa_iic.h b/arch/ppc64/kernel/bpa_iic.h new file mode 100644 index 000000000000..6833c3022166 --- /dev/null +++ b/arch/ppc64/kernel/bpa_iic.h @@ -0,0 +1,62 @@ +#ifndef ASM_BPA_IIC_H +#define ASM_BPA_IIC_H +#ifdef __KERNEL__ +/* + * Mapping of IIC pending bits into per-node + * interrupt numbers. + * + * IRQ FF CC SS PP FF CC SS PP Description + * + * 00-3f 80 02 +0 00 - 80 02 +0 3f South Bridge + * 00-3f 80 02 +b 00 - 80 02 +b 3f South Bridge + * 41-4a 80 00 +1 ** - 80 00 +a ** SPU Class 0 + * 51-5a 80 01 +1 ** - 80 01 +a ** SPU Class 1 + * 61-6a 80 02 +1 ** - 80 02 +a ** SPU Class 2 + * 70-7f C0 ** ** 00 - C0 ** ** 0f IPI + * + * F flags + * C class + * S source + * P Priority + * + node number + * * don't care + * + * A node consists of a Broadband Engine and an optional + * south bridge device providing a maximum of 64 IRQs. + * The south bridge may be connected to either IOIF0 + * or IOIF1. + * Each SPE is represented as three IRQ lines, one per + * interrupt class. + * 16 IRQ numbers are reserved for inter processor + * interruptions, although these are only used in the + * range of the first node. + * + * This scheme needs 128 IRQ numbers per BIF node ID, + * which means that with the total of 512 lines + * available, we can have a maximum of four nodes. + */ + +enum { + IIC_EXT_OFFSET = 0x00, /* Start of south bridge IRQs */ + IIC_NUM_EXT = 0x40, /* Number of south bridge IRQs */ + IIC_SPE_OFFSET = 0x40, /* Start of SPE interrupts */ + IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class */ + IIC_IPI_OFFSET = 0x70, /* Start of IPI IRQs */ + IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */ + IIC_NODE_STRIDE = 0x80, /* Total IRQs per node */ +}; + +extern void iic_init_IRQ(void); +extern int iic_get_irq(struct pt_regs *regs); +extern void iic_cause_IPI(int cpu, int mesg); +extern void iic_request_IPIs(void); +extern void iic_setup_cpu(void); +extern void iic_local_enable(void); +extern void iic_local_disable(void); + + +extern void spider_init_IRQ(void); +extern int spider_get_irq(unsigned long int_pending); + +#endif +#endif /* ASM_BPA_IIC_H */ diff --git a/arch/ppc64/kernel/bpa_iommu.c b/arch/ppc64/kernel/bpa_iommu.c new file mode 100644 index 000000000000..f33a7bccb0d7 --- /dev/null +++ b/arch/ppc64/kernel/bpa_iommu.c @@ -0,0 +1,377 @@ +/* + * IOMMU implementation for Broadband Processor Architecture + * We just establish a linear mapping at boot by setting all the + * IOPT cache entries in the CPU. + * The mapping functions should be identical to pci_direct_iommu, + * except for the handling of the high order bit that is required + * by the Spider bridge. These should be split into a separate + * file at the point where we get a different bridge chip. + * + * Copyright (C) 2005 IBM Deutschland Entwicklung GmbH, + * Arnd Bergmann + * + * Based on linear mapping + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" +#include "bpa_iommu.h" + +static inline unsigned long +get_iopt_entry(unsigned long real_address, unsigned long ioid, + unsigned long prot) +{ + return (prot & IOPT_PROT_MASK) + | (IOPT_COHERENT) + | (IOPT_ORDER_VC) + | (real_address & IOPT_RPN_MASK) + | (ioid & IOPT_IOID_MASK); +} + +typedef struct { + unsigned long val; +} ioste; + +static inline ioste +mk_ioste(unsigned long val) +{ + ioste ioste = { .val = val, }; + return ioste; +} + +static inline ioste +get_iost_entry(unsigned long iopt_base, unsigned long io_address, unsigned page_size) +{ + unsigned long ps; + unsigned long iostep; + unsigned long nnpt; + unsigned long shift; + + switch (page_size) { + case 0x1000000: + ps = IOST_PS_16M; + nnpt = 0; /* one page per segment */ + shift = 5; /* segment has 16 iopt entries */ + break; + + case 0x100000: + ps = IOST_PS_1M; + nnpt = 0; /* one page per segment */ + shift = 1; /* segment has 256 iopt entries */ + break; + + case 0x10000: + ps = IOST_PS_64K; + nnpt = 0x07; /* 8 pages per io page table */ + shift = 0; /* all entries are used */ + break; + + case 0x1000: + ps = IOST_PS_4K; + nnpt = 0x7f; /* 128 pages per io page table */ + shift = 0; /* all entries are used */ + break; + + default: /* not a known compile time constant */ + BUILD_BUG_ON(1); + break; + } + + iostep = iopt_base + + /* need 8 bytes per iopte */ + (((io_address / page_size * 8) + /* align io page tables on 4k page boundaries */ + << shift) + /* nnpt+1 pages go into each iopt */ + & ~(nnpt << 12)); + + nnpt++; /* this seems to work, but the documentation is not clear + about wether we put nnpt or nnpt-1 into the ioste bits. + In theory, this can't work for 4k pages. */ + return mk_ioste(IOST_VALID_MASK + | (iostep & IOST_PT_BASE_MASK) + | ((nnpt << 5) & IOST_NNPT_MASK) + | (ps & IOST_PS_MASK)); +} + +/* compute the address of an io pte */ +static inline unsigned long +get_ioptep(ioste iost_entry, unsigned long io_address) +{ + unsigned long iopt_base; + unsigned long page_size; + unsigned long page_number; + unsigned long iopt_offset; + + iopt_base = iost_entry.val & IOST_PT_BASE_MASK; + page_size = iost_entry.val & IOST_PS_MASK; + + /* decode page size to compute page number */ + page_number = (io_address & 0x0fffffff) >> (10 + 2 * page_size); + /* page number is an offset into the io page table */ + iopt_offset = (page_number << 3) & 0x7fff8ul; + return iopt_base + iopt_offset; +} + +/* compute the tag field of the iopt cache entry */ +static inline unsigned long +get_ioc_tag(ioste iost_entry, unsigned long io_address) +{ + unsigned long iopte = get_ioptep(iost_entry, io_address); + + return IOPT_VALID_MASK + | ((iopte & 0x00000000000000ff8ul) >> 3) + | ((iopte & 0x0000003fffffc0000ul) >> 9); +} + +/* compute the hashed 6 bit index for the 4-way associative pte cache */ +static inline unsigned long +get_ioc_hash(ioste iost_entry, unsigned long io_address) +{ + unsigned long iopte = get_ioptep(iost_entry, io_address); + + return ((iopte & 0x000000000000001f8ul) >> 3) + ^ ((iopte & 0x00000000000020000ul) >> 17) + ^ ((iopte & 0x00000000000010000ul) >> 15) + ^ ((iopte & 0x00000000000008000ul) >> 13) + ^ ((iopte & 0x00000000000004000ul) >> 11) + ^ ((iopte & 0x00000000000002000ul) >> 9) + ^ ((iopte & 0x00000000000001000ul) >> 7); +} + +/* same as above, but pretend that we have a simpler 1-way associative + pte cache with an 8 bit index */ +static inline unsigned long +get_ioc_hash_1way(ioste iost_entry, unsigned long io_address) +{ + unsigned long iopte = get_ioptep(iost_entry, io_address); + + return ((iopte & 0x000000000000001f8ul) >> 3) + ^ ((iopte & 0x00000000000020000ul) >> 17) + ^ ((iopte & 0x00000000000010000ul) >> 15) + ^ ((iopte & 0x00000000000008000ul) >> 13) + ^ ((iopte & 0x00000000000004000ul) >> 11) + ^ ((iopte & 0x00000000000002000ul) >> 9) + ^ ((iopte & 0x00000000000001000ul) >> 7) + ^ ((iopte & 0x0000000000000c000ul) >> 8); +} + +static inline ioste +get_iost_cache(void __iomem *base, unsigned long index) +{ + unsigned long __iomem *p = (base + IOC_ST_CACHE_DIR); + return mk_ioste(in_be64(&p[index])); +} + +static inline void +set_iost_cache(void __iomem *base, unsigned long index, ioste ste) +{ + unsigned long __iomem *p = (base + IOC_ST_CACHE_DIR); + pr_debug("ioste %02lx was %016lx, store %016lx", index, + get_iost_cache(base, index).val, ste.val); + out_be64(&p[index], ste.val); + pr_debug(" now %016lx\n", get_iost_cache(base, index).val); +} + +static inline unsigned long +get_iopt_cache(void __iomem *base, unsigned long index, unsigned long *tag) +{ + unsigned long __iomem *tags = (void *)(base + IOC_PT_CACHE_DIR); + unsigned long __iomem *p = (void *)(base + IOC_PT_CACHE_REG); + + *tag = tags[index]; + rmb(); + return *p; +} + +static inline void +set_iopt_cache(void __iomem *base, unsigned long index, + unsigned long tag, unsigned long val) +{ + unsigned long __iomem *tags = base + IOC_PT_CACHE_DIR; + unsigned long __iomem *p = base + IOC_PT_CACHE_REG; + pr_debug("iopt %02lx was v%016lx/t%016lx, store v%016lx/t%016lx\n", + index, get_iopt_cache(base, index, &oldtag), oldtag, val, tag); + + out_be64(p, val); + out_be64(&tags[index], tag); +} + +static inline void +set_iost_origin(void __iomem *base) +{ + unsigned long __iomem *p = base + IOC_ST_ORIGIN; + unsigned long origin = IOSTO_ENABLE | IOSTO_SW; + + pr_debug("iost_origin %016lx, now %016lx\n", in_be64(p), origin); + out_be64(p, origin); +} + +static inline void +set_iocmd_config(void __iomem *base) +{ + unsigned long __iomem *p = base + 0xc00; + unsigned long conf; + + conf = in_be64(p); + pr_debug("iost_conf %016lx, now %016lx\n", conf, conf | IOCMD_CONF_TE); + out_be64(p, conf | IOCMD_CONF_TE); +} + +/* FIXME: get these from the device tree */ +#define ioc_base 0x20000511000ull +#define ioc_mmio_base 0x20000510000ull +#define ioid 0x48a +#define iopt_phys_offset (- 0x20000000) /* We have a 512MB offset from the SB */ +#define io_page_size 0x1000000 + +static unsigned long map_iopt_entry(unsigned long address) +{ + switch (address >> 20) { + case 0x600: + address = 0x24020000000ull; /* spider i/o */ + break; + default: + address += iopt_phys_offset; + break; + } + + return get_iopt_entry(address, ioid, IOPT_PROT_RW); +} + +static void iommu_bus_setup_null(struct pci_bus *b) { } +static void iommu_dev_setup_null(struct pci_dev *d) { } + +/* initialize the iommu to support a simple linear mapping + * for each DMA window used by any device. For now, we + * happen to know that there is only one DMA window in use, + * starting at iopt_phys_offset. */ +static void bpa_map_iommu(void) +{ + unsigned long address; + void __iomem *base; + ioste ioste; + unsigned long index; + + base = __ioremap(ioc_base, 0x1000, _PAGE_NO_CACHE); + pr_debug("%lx mapped to %p\n", ioc_base, base); + set_iocmd_config(base); + iounmap(base); + + base = __ioremap(ioc_mmio_base, 0x1000, _PAGE_NO_CACHE); + pr_debug("%lx mapped to %p\n", ioc_mmio_base, base); + + set_iost_origin(base); + + for (address = 0; address < 0x100000000ul; address += io_page_size) { + ioste = get_iost_entry(0x10000000000ul, address, io_page_size); + if ((address & 0xfffffff) == 0) /* segment start */ + set_iost_cache(base, address >> 28, ioste); + index = get_ioc_hash_1way(ioste, address); + pr_debug("addr %08lx, index %02lx, ioste %016lx\n", + address, index, ioste.val); + set_iopt_cache(base, + get_ioc_hash_1way(ioste, address), + get_ioc_tag(ioste, address), + map_iopt_entry(address)); + } + iounmap(base); +} + + +static void *bpa_alloc_coherent(struct device *hwdev, size_t size, + dma_addr_t *dma_handle, unsigned int __nocast flag) +{ + void *ret; + + ret = (void *)__get_free_pages(flag, get_order(size)); + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_abs(ret) | BPA_DMA_VALID; + } + return ret; +} + +static void bpa_free_coherent(struct device *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} + +static dma_addr_t bpa_map_single(struct device *hwdev, void *ptr, + size_t size, enum dma_data_direction direction) +{ + return virt_to_abs(ptr) | BPA_DMA_VALID; +} + +static void bpa_unmap_single(struct device *hwdev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction direction) +{ +} + +static int bpa_map_sg(struct device *hwdev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ + int i; + + for (i = 0; i < nents; i++, sg++) { + sg->dma_address = (page_to_phys(sg->page) + sg->offset) + | BPA_DMA_VALID; + sg->dma_length = sg->length; + } + + return nents; +} + +static void bpa_unmap_sg(struct device *hwdev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ +} + +static int bpa_dma_supported(struct device *dev, u64 mask) +{ + return mask < 0x100000000ull; +} + +void bpa_init_iommu(void) +{ + bpa_map_iommu(); + + /* Direct I/O, IOMMU off */ + ppc_md.iommu_dev_setup = iommu_dev_setup_null; + ppc_md.iommu_bus_setup = iommu_bus_setup_null; + + pci_dma_ops.alloc_coherent = bpa_alloc_coherent; + pci_dma_ops.free_coherent = bpa_free_coherent; + pci_dma_ops.map_single = bpa_map_single; + pci_dma_ops.unmap_single = bpa_unmap_single; + pci_dma_ops.map_sg = bpa_map_sg; + pci_dma_ops.unmap_sg = bpa_unmap_sg; + pci_dma_ops.dma_supported = bpa_dma_supported; +} diff --git a/arch/ppc64/kernel/bpa_iommu.h b/arch/ppc64/kernel/bpa_iommu.h new file mode 100644 index 000000000000..e547d77dfa04 --- /dev/null +++ b/arch/ppc64/kernel/bpa_iommu.h @@ -0,0 +1,65 @@ +#ifndef BPA_IOMMU_H +#define BPA_IOMMU_H + +/* some constants */ +enum { + /* segment table entries */ + IOST_VALID_MASK = 0x8000000000000000ul, + IOST_TAG_MASK = 0x3000000000000000ul, + IOST_PT_BASE_MASK = 0x000003fffffff000ul, + IOST_NNPT_MASK = 0x0000000000000fe0ul, + IOST_PS_MASK = 0x000000000000000ful, + + IOST_PS_4K = 0x1, + IOST_PS_64K = 0x3, + IOST_PS_1M = 0x5, + IOST_PS_16M = 0x7, + + /* iopt tag register */ + IOPT_VALID_MASK = 0x0000000200000000ul, + IOPT_TAG_MASK = 0x00000001fffffffful, + + /* iopt cache register */ + IOPT_PROT_MASK = 0xc000000000000000ul, + IOPT_PROT_NONE = 0x0000000000000000ul, + IOPT_PROT_READ = 0x4000000000000000ul, + IOPT_PROT_WRITE = 0x8000000000000000ul, + IOPT_PROT_RW = 0xc000000000000000ul, + IOPT_COHERENT = 0x2000000000000000ul, + + IOPT_ORDER_MASK = 0x1800000000000000ul, + /* order access to same IOID/VC on same address */ + IOPT_ORDER_ADDR = 0x0800000000000000ul, + /* similar, but only after a write access */ + IOPT_ORDER_WRITES = 0x1000000000000000ul, + /* Order all accesses to same IOID/VC */ + IOPT_ORDER_VC = 0x1800000000000000ul, + + IOPT_RPN_MASK = 0x000003fffffff000ul, + IOPT_HINT_MASK = 0x0000000000000800ul, + IOPT_IOID_MASK = 0x00000000000007fful, + + IOSTO_ENABLE = 0x8000000000000000ul, + IOSTO_ORIGIN = 0x000003fffffff000ul, + IOSTO_HW = 0x0000000000000800ul, + IOSTO_SW = 0x0000000000000400ul, + + IOCMD_CONF_TE = 0x0000800000000000ul, + + /* memory mapped registers */ + IOC_PT_CACHE_DIR = 0x000, + IOC_ST_CACHE_DIR = 0x800, + IOC_PT_CACHE_REG = 0x910, + IOC_ST_ORIGIN = 0x918, + IOC_CONF = 0x930, + + /* The high bit needs to be set on every DMA address, + only 2GB are addressable */ + BPA_DMA_VALID = 0x80000000, + BPA_DMA_MASK = 0x7fffffff, +}; + + +void bpa_init_iommu(void); + +#endif diff --git a/arch/ppc64/kernel/bpa_nvram.c b/arch/ppc64/kernel/bpa_nvram.c new file mode 100644 index 000000000000..06a119cfceb5 --- /dev/null +++ b/arch/ppc64/kernel/bpa_nvram.c @@ -0,0 +1,118 @@ +/* + * NVRAM for CPBW + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static void __iomem *bpa_nvram_start; +static long bpa_nvram_len; +static spinlock_t bpa_nvram_lock = SPIN_LOCK_UNLOCKED; + +static ssize_t bpa_nvram_read(char *buf, size_t count, loff_t *index) +{ + unsigned long flags; + + if (*index >= bpa_nvram_len) + return 0; + if (*index + count > bpa_nvram_len) + count = bpa_nvram_len - *index; + + spin_lock_irqsave(&bpa_nvram_lock, flags); + + memcpy_fromio(buf, bpa_nvram_start + *index, count); + + spin_unlock_irqrestore(&bpa_nvram_lock, flags); + + *index += count; + return count; +} + +static ssize_t bpa_nvram_write(char *buf, size_t count, loff_t *index) +{ + unsigned long flags; + + if (*index >= bpa_nvram_len) + return 0; + if (*index + count > bpa_nvram_len) + count = bpa_nvram_len - *index; + + spin_lock_irqsave(&bpa_nvram_lock, flags); + + memcpy_toio(bpa_nvram_start + *index, buf, count); + + spin_unlock_irqrestore(&bpa_nvram_lock, flags); + + *index += count; + return count; +} + +static ssize_t bpa_nvram_get_size(void) +{ + return bpa_nvram_len; +} + +int __init bpa_nvram_init(void) +{ + struct device_node *nvram_node; + unsigned long *buffer; + int proplen; + unsigned long nvram_addr; + int ret; + + ret = -ENODEV; + nvram_node = of_find_node_by_type(NULL, "nvram"); + if (!nvram_node) + goto out; + + ret = -EIO; + buffer = (unsigned long *)get_property(nvram_node, "reg", &proplen); + if (proplen != 2*sizeof(unsigned long)) + goto out; + + ret = -ENODEV; + nvram_addr = buffer[0]; + bpa_nvram_len = buffer[1]; + if ( (!bpa_nvram_len) || (!nvram_addr) ) + goto out; + + bpa_nvram_start = ioremap(nvram_addr, bpa_nvram_len); + if (!bpa_nvram_start) + goto out; + + printk(KERN_INFO "BPA NVRAM, %luk mapped to %p\n", + bpa_nvram_len >> 10, bpa_nvram_start); + + ppc_md.nvram_read = bpa_nvram_read; + ppc_md.nvram_write = bpa_nvram_write; + ppc_md.nvram_size = bpa_nvram_get_size; + +out: + of_node_put(nvram_node); + return ret; +} diff --git a/arch/ppc64/kernel/bpa_setup.c b/arch/ppc64/kernel/bpa_setup.c new file mode 100644 index 000000000000..57b3db66f458 --- /dev/null +++ b/arch/ppc64/kernel/bpa_setup.c @@ -0,0 +1,140 @@ +/* + * linux/arch/ppc/kernel/bpa_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 + * Modified by BPA Team, IBM Deutschland Entwicklung GmbH + * + * 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. + */ +#undef DEBUG + +#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 "pci.h" +#include "bpa_iic.h" +#include "bpa_iommu.h" + +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +void bpa_get_cpuinfo(struct seq_file *m) +{ + struct device_node *root; + const char *model = ""; + + root = of_find_node_by_path("/"); + if (root) + model = get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: BPA %s\n", model); + of_node_put(root); +} + +static void bpa_progress(char *s, unsigned short hex) +{ + printk("*** %04x : %s\n", hex, s ? s : ""); +} + +static void __init bpa_setup_arch(void) +{ + ppc_md.init_IRQ = iic_init_IRQ; + ppc_md.get_irq = iic_get_irq; + +#ifdef CONFIG_SMP + smp_init_pSeries(); +#endif + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + if (ROOT_DEV == 0) { + printk("No ramdisk, default root is /dev/hda2\n"); + ROOT_DEV = Root_HDA2; + } + + /* Find and initialize PCI host bridges */ + init_pci_config_tokens(); + find_and_init_phbs(); + spider_init_IRQ(); +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + bpa_nvram_init(); +} + +/* + * Early initialization. Relocation is on but do not reference unbolted pages + */ +static void __init bpa_init_early(void) +{ + DBG(" -> bpa_init_early()\n"); + + hpte_init_native(); + + bpa_init_iommu(); + + ppc64_interrupt_controller = IC_BPA_IIC; + + DBG(" <- bpa_init_early()\n"); +} + + +static int __init bpa_probe(int platform) +{ + if (platform != PLATFORM_BPA) + return 0; + + return 1; +} + +struct machdep_calls __initdata bpa_md = { + .probe = bpa_probe, + .setup_arch = bpa_setup_arch, + .init_early = bpa_init_early, + .get_cpuinfo = bpa_get_cpuinfo, + .restart = rtas_restart, + .power_off = rtas_power_off, + .halt = rtas_halt, + .get_boot_time = rtas_get_boot_time, + .get_rtc_time = rtas_get_rtc_time, + .set_rtc_time = rtas_set_rtc_time, + .calibrate_decr = generic_calibrate_decr, + .progress = bpa_progress, +}; diff --git a/arch/ppc64/kernel/cpu_setup_power4.S b/arch/ppc64/kernel/cpu_setup_power4.S index 3bd951820850..42fc08cf87a0 100644 --- a/arch/ppc64/kernel/cpu_setup_power4.S +++ b/arch/ppc64/kernel/cpu_setup_power4.S @@ -73,7 +73,21 @@ _GLOBAL(__970_cpu_preinit) _GLOBAL(__setup_cpu_power4) blr - + +_GLOBAL(__setup_cpu_be) + /* Set large page sizes LP=0: 16MB, LP=1: 64KB */ + addi r3, 0, 0 + ori r3, r3, HID6_LB + sldi r3, r3, 32 + nor r3, r3, r3 + mfspr r4, SPRN_HID6 + and r4, r4, r3 + addi r3, 0, 0x02000 + sldi r3, r3, 32 + or r4, r4, r3 + mtspr SPRN_HID6, r4 + blr + _GLOBAL(__setup_cpu_ppc970) mfspr r0,SPRN_HID0 li r11,5 /* clear DOZE and SLEEP */ diff --git a/arch/ppc64/kernel/cputable.c b/arch/ppc64/kernel/cputable.c index 8644a8648058..1d162c7c59df 100644 --- a/arch/ppc64/kernel/cputable.c +++ b/arch/ppc64/kernel/cputable.c @@ -34,6 +34,7 @@ EXPORT_SYMBOL(cur_cpu_spec); extern void __setup_cpu_power3(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_power4(unsigned long offset, struct cpu_spec* spec); extern void __setup_cpu_ppc970(unsigned long offset, struct cpu_spec* spec); +extern void __setup_cpu_be(unsigned long offset, struct cpu_spec* spec); /* We only set the altivec features if the kernel was compiled with altivec @@ -162,6 +163,16 @@ struct cpu_spec cpu_specs[] = { __setup_cpu_power4, COMMON_PPC64_FW }, + { /* BE DD1.x */ + 0xffff0000, 0x00700000, "Broadband Engine", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | + CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_ALTIVEC_COMP | + CPU_FTR_SMT, + COMMON_USER_PPC64 | PPC_FEATURE_HAS_ALTIVEC_COMP, + 128, 128, + __setup_cpu_be, + COMMON_PPC64_FW + }, { /* default match */ 0x00000000, 0x00000000, "POWER4 (compatible)", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | diff --git a/arch/ppc64/kernel/iSeries_setup.c b/arch/ppc64/kernel/iSeries_setup.c index b31962436fe3..86966ce76b58 100644 --- a/arch/ppc64/kernel/iSeries_setup.c +++ b/arch/ppc64/kernel/iSeries_setup.c @@ -671,9 +671,6 @@ static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr) } } -extern unsigned long ppc_proc_freq; -extern unsigned long ppc_tb_freq; - /* * Document me. */ @@ -772,8 +769,6 @@ static void iSeries_halt(void) mf_power_off(); } -extern void setup_default_decr(void); - /* * void __init iSeries_calibrate_decr() * diff --git a/arch/ppc64/kernel/irq.c b/arch/ppc64/kernel/irq.c index d860467b8f09..3defc8c33adf 100644 --- a/arch/ppc64/kernel/irq.c +++ b/arch/ppc64/kernel/irq.c @@ -395,6 +395,9 @@ int virt_irq_create_mapping(unsigned int real_irq) if (ppc64_interrupt_controller == IC_OPEN_PIC) return real_irq; /* no mapping for openpic (for now) */ + if (ppc64_interrupt_controller == IC_BPA_IIC) + return real_irq; /* no mapping for iic either */ + /* don't map interrupts < MIN_VIRT_IRQ */ if (real_irq < MIN_VIRT_IRQ) { virt_irq_to_real_map[real_irq] = real_irq; diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c index e950a2058a19..782ce3efa2c1 100644 --- a/arch/ppc64/kernel/kprobes.c +++ b/arch/ppc64/kernel/kprobes.c @@ -32,15 +32,14 @@ #include #include #include +#include #include #include -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 - static struct kprobe *current_kprobe; static unsigned long kprobe_status, kprobe_saved_msr; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_status_prev, kprobe_saved_msr_prev; static struct pt_regs jprobe_saved_regs; int arch_prepare_kprobe(struct kprobe *p) @@ -61,18 +60,27 @@ int arch_prepare_kprobe(struct kprobe *p) void arch_copy_kprobe(struct kprobe *p) { memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); + p->opcode = *p->addr; +} + +void arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); +} + +void arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); } void arch_remove_kprobe(struct kprobe *p) { } -static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) -{ - *p->addr = p->opcode; - regs->nip = (unsigned long)p->addr; -} - static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) { regs->msr |= MSR_SE; @@ -83,6 +91,20 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) regs->nip = (unsigned long)&p->ainsn.insn; } +static inline void save_previous_kprobe(void) +{ + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; + kprobe_saved_msr_prev = kprobe_saved_msr; +} + +static inline void restore_previous_kprobe(void) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; + kprobe_saved_msr = kprobe_saved_msr_prev; +} + static inline int kprobe_handler(struct pt_regs *regs) { struct kprobe *p; @@ -101,8 +123,19 @@ static inline int kprobe_handler(struct pt_regs *regs) unlock_kprobes(); goto no_kprobe; } - disarm_kprobe(p, regs); - ret = 1; + /* We have reentered the kprobe_handler(), since + * another probe was hit while within the handler. + * We here save the original kprobes variables and + * just single step on the instruction of the new probe + * without calling any user handlers. + */ + save_previous_kprobe(); + current_kprobe = p; + kprobe_saved_msr = regs->msr; + p->nmissed++; + prepare_singlestep(p, regs); + kprobe_status = KPROBE_REENTER; + return 1; } else { p = current_kprobe; if (p->break_handler && p->break_handler(p, regs)) { @@ -184,13 +217,21 @@ static inline int post_kprobe_handler(struct pt_regs *regs) if (!kprobe_running()) return 0; - if (current_kprobe->post_handler) + if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { + kprobe_status = KPROBE_HIT_SSDONE; current_kprobe->post_handler(current_kprobe, regs, 0); + } resume_execution(current_kprobe, regs); regs->msr |= kprobe_saved_msr; + /*Restore back the original saved kprobes variables and continue. */ + if (kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(); + goto out; + } unlock_kprobes(); +out: preempt_enable_no_resched(); /* diff --git a/arch/ppc64/kernel/maple_setup.c b/arch/ppc64/kernel/maple_setup.c index 8cf95a27178e..da8900b51f40 100644 --- a/arch/ppc64/kernel/maple_setup.c +++ b/arch/ppc64/kernel/maple_setup.c @@ -78,17 +78,77 @@ extern int maple_pci_get_legacy_ide_irq(struct pci_dev *dev, int channel); extern void generic_find_legacy_serial_ports(u64 *physport, unsigned int *default_speed); - static void maple_restart(char *cmd) { + unsigned int maple_nvram_base; + unsigned int maple_nvram_offset; + unsigned int maple_nvram_command; + struct device_node *rtcs; + + /* find NVRAM device */ + rtcs = find_compatible_devices("nvram", "AMD8111"); + if (rtcs && rtcs->addrs) { + maple_nvram_base = rtcs->addrs[0].address; + } else { + printk(KERN_EMERG "Maple: Unable to find NVRAM\n"); + printk(KERN_EMERG "Maple: Manual Restart Required\n"); + return; + } + + /* find service processor device */ + rtcs = find_devices("service-processor"); + if (!rtcs) { + printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); + printk(KERN_EMERG "Maple: Manual Restart Required\n"); + return; + } + maple_nvram_offset = *(unsigned int*) get_property(rtcs, + "restart-addr", NULL); + maple_nvram_command = *(unsigned int*) get_property(rtcs, + "restart-value", NULL); + + /* send command */ + outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset); + for (;;) ; } static void maple_power_off(void) { + unsigned int maple_nvram_base; + unsigned int maple_nvram_offset; + unsigned int maple_nvram_command; + struct device_node *rtcs; + + /* find NVRAM device */ + rtcs = find_compatible_devices("nvram", "AMD8111"); + if (rtcs && rtcs->addrs) { + maple_nvram_base = rtcs->addrs[0].address; + } else { + printk(KERN_EMERG "Maple: Unable to find NVRAM\n"); + printk(KERN_EMERG "Maple: Manual Power-Down Required\n"); + return; + } + + /* find service processor device */ + rtcs = find_devices("service-processor"); + if (!rtcs) { + printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); + printk(KERN_EMERG "Maple: Manual Power-Down Required\n"); + return; + } + maple_nvram_offset = *(unsigned int*) get_property(rtcs, + "power-off-addr", NULL); + maple_nvram_command = *(unsigned int*) get_property(rtcs, + "power-off-value", NULL); + + /* send command */ + outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset); + for (;;) ; } static void maple_halt(void) { + maple_power_off(); } #ifdef CONFIG_SMP @@ -235,6 +295,6 @@ struct machdep_calls __initdata maple_md = { .get_boot_time = maple_get_boot_time, .set_rtc_time = maple_set_rtc_time, .get_rtc_time = maple_get_rtc_time, - .calibrate_decr = maple_calibrate_decr, + .calibrate_decr = generic_calibrate_decr, .progress = maple_progress, }; diff --git a/arch/ppc64/kernel/maple_time.c b/arch/ppc64/kernel/maple_time.c index 07ce7895b43d..d65210abcd03 100644 --- a/arch/ppc64/kernel/maple_time.c +++ b/arch/ppc64/kernel/maple_time.c @@ -42,11 +42,8 @@ #define DBG(x...) #endif -extern void setup_default_decr(void); extern void GregorianDay(struct rtc_time * tm); -extern unsigned long ppc_tb_freq; -extern unsigned long ppc_proc_freq; static int maple_rtc_addr; static int maple_clock_read(int addr) @@ -176,51 +173,3 @@ void __init maple_get_boot_time(struct rtc_time *tm) maple_get_rtc_time(tm); } -/* XXX FIXME: Some sane defaults: 125 MHz timebase, 1GHz processor */ -#define DEFAULT_TB_FREQ 125000000UL -#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) - -void __init maple_calibrate_decr(void) -{ - struct device_node *cpu; - struct div_result divres; - unsigned int *fp = NULL; - - /* - * The cpu node should have a timebase-frequency property - * to tell us the rate at which the decrementer counts. - */ - cpu = of_find_node_by_type(NULL, "cpu"); - - ppc_tb_freq = DEFAULT_TB_FREQ; - if (cpu != 0) - fp = (unsigned int *)get_property(cpu, "timebase-frequency", NULL); - if (fp != NULL) - ppc_tb_freq = *fp; - else - printk(KERN_ERR "WARNING: Estimating decrementer frequency (not found)\n"); - fp = NULL; - ppc_proc_freq = DEFAULT_PROC_FREQ; - if (cpu != 0) - fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL); - if (fp != NULL) - ppc_proc_freq = *fp; - else - printk(KERN_ERR "WARNING: Estimating processor frequency (not found)\n"); - - of_node_put(cpu); - - printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", - ppc_tb_freq/1000000, ppc_tb_freq%1000000); - printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", - ppc_proc_freq/1000000, ppc_proc_freq%1000000); - - tb_ticks_per_jiffy = ppc_tb_freq / HZ; - tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; - tb_ticks_per_usec = ppc_tb_freq / 1000000; - tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); - div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); - tb_to_xs = divres.result_low; - - setup_default_decr(); -} diff --git a/arch/ppc64/kernel/mpic.h b/arch/ppc64/kernel/mpic.h index 571b3c99e062..63e177143eac 100644 --- a/arch/ppc64/kernel/mpic.h +++ b/arch/ppc64/kernel/mpic.h @@ -265,3 +265,6 @@ extern void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask); extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs); /* This one gets to the primary mpic */ extern int mpic_get_irq(struct pt_regs *regs); + +/* global mpic for pSeries */ +extern struct mpic *pSeries_mpic; diff --git a/arch/ppc64/kernel/pSeries_pci.c b/arch/ppc64/kernel/pSeries_pci.c index 0b1cca281408..1f5f141fb7a1 100644 --- a/arch/ppc64/kernel/pSeries_pci.c +++ b/arch/ppc64/kernel/pSeries_pci.c @@ -1,13 +1,11 @@ /* - * pSeries_pci.c + * arch/ppc64/kernel/pSeries_pci.c * * Copyright (C) 2001 Dave Engebretsen, IBM Corporation * Copyright (C) 2003 Anton Blanchard , IBM * * 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 @@ -23,430 +21,18 @@ * 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 "mpic.h" #include "pci.h" -/* RTAS tokens */ -static int read_pci_config; -static int write_pci_config; -static int ibm_read_pci_config; -static int ibm_write_pci_config; - -static int s7a_workaround; - -extern struct mpic *pSeries_mpic; - -static int config_access_valid(struct device_node *dn, int where) -{ - if (where < 256) - return 1; - if (where < 4096 && dn->pci_ext_config_space) - return 1; - - return 0; -} - -static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val) -{ - int returnval = -1; - unsigned long buid, addr; - int ret; - - if (!dn) - return PCIBIOS_DEVICE_NOT_FOUND; - if (!config_access_valid(dn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - addr = ((where & 0xf00) << 20) | (dn->busno << 16) | - (dn->devfn << 8) | (where & 0xff); - buid = dn->phb->buid; - if (buid) { - ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, - addr, buid >> 32, buid & 0xffffffff, size); - } else { - ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); - } - *val = returnval; - - if (ret) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (returnval == EEH_IO_ERROR_VALUE(size) - && eeh_dn_check_failure (dn, NULL)) - return PCIBIOS_DEVICE_NOT_FOUND; - - return PCIBIOS_SUCCESSFUL; -} - -static int rtas_pci_read_config(struct pci_bus *bus, - unsigned int devfn, - int where, int size, u32 *val) -{ - struct device_node *busdn, *dn; - - if (bus->self) - busdn = pci_device_to_OF_node(bus->self); - else - busdn = bus->sysdata; /* must be a phb */ - - /* Search only direct children of the bus */ - for (dn = busdn->child; dn; dn = dn->sibling) - if (dn->devfn == devfn) - return rtas_read_config(dn, where, size, val); - return PCIBIOS_DEVICE_NOT_FOUND; -} - -static int rtas_write_config(struct device_node *dn, int where, int size, u32 val) -{ - unsigned long buid, addr; - int ret; - - if (!dn) - return PCIBIOS_DEVICE_NOT_FOUND; - if (!config_access_valid(dn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - addr = ((where & 0xf00) << 20) | (dn->busno << 16) | - (dn->devfn << 8) | (where & 0xff); - buid = dn->phb->buid; - if (buid) { - ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val); - } else { - ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); - } - - if (ret) - return PCIBIOS_DEVICE_NOT_FOUND; - - return PCIBIOS_SUCCESSFUL; -} - -static int rtas_pci_write_config(struct pci_bus *bus, - unsigned int devfn, - int where, int size, u32 val) -{ - struct device_node *busdn, *dn; - - if (bus->self) - busdn = pci_device_to_OF_node(bus->self); - else - busdn = bus->sysdata; /* must be a phb */ - - /* Search only direct children of the bus */ - for (dn = busdn->child; dn; dn = dn->sibling) - if (dn->devfn == devfn) - return rtas_write_config(dn, where, size, val); - return PCIBIOS_DEVICE_NOT_FOUND; -} - -struct pci_ops rtas_pci_ops = { - rtas_pci_read_config, - rtas_pci_write_config -}; - -int is_python(struct device_node *dev) -{ - char *model = (char *)get_property(dev, "model", NULL); - - if (model && strstr(model, "Python")) - return 1; - - return 0; -} - -static int get_phb_reg_prop(struct device_node *dev, - unsigned int addr_size_words, - struct reg_property64 *reg) -{ - unsigned int *ui_ptr = NULL, len; - - /* Found a PHB, now figure out where his registers are mapped. */ - ui_ptr = (unsigned int *)get_property(dev, "reg", &len); - if (ui_ptr == NULL) - return 1; - - if (addr_size_words == 1) { - reg->address = ((struct reg_property32 *)ui_ptr)->address; - reg->size = ((struct reg_property32 *)ui_ptr)->size; - } else { - *reg = *((struct reg_property64 *)ui_ptr); - } - - return 0; -} - -static void python_countermeasures(struct device_node *dev, - unsigned int addr_size_words) -{ - struct reg_property64 reg_struct; - void __iomem *chip_regs; - volatile u32 val; - - if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) - return; - - /* Python's register file is 1 MB in size. */ - chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); - - /* - * Firmware doesn't always clear this bit which is critical - * for good performance - Anton - */ - -#define PRG_CL_RESET_VALID 0x00010000 - - val = in_be32(chip_regs + 0xf6030); - if (val & PRG_CL_RESET_VALID) { - printk(KERN_INFO "Python workaround: "); - val &= ~PRG_CL_RESET_VALID; - out_be32(chip_regs + 0xf6030, val); - /* - * We must read it back for changes to - * take effect - */ - val = in_be32(chip_regs + 0xf6030); - printk("reg0: %x\n", val); - } - - iounmap(chip_regs); -} - -void __init init_pci_config_tokens (void) -{ - read_pci_config = rtas_token("read-pci-config"); - write_pci_config = rtas_token("write-pci-config"); - ibm_read_pci_config = rtas_token("ibm,read-pci-config"); - ibm_write_pci_config = rtas_token("ibm,write-pci-config"); -} - -unsigned long __devinit get_phb_buid (struct device_node *phb) -{ - int addr_cells; - unsigned int *buid_vals; - unsigned int len; - unsigned long buid; - - if (ibm_read_pci_config == -1) return 0; - - /* PHB's will always be children of the root node, - * or so it is promised by the current firmware. */ - if (phb->parent == NULL) - return 0; - if (phb->parent->parent) - return 0; - - buid_vals = (unsigned int *) get_property(phb, "reg", &len); - if (buid_vals == NULL) - return 0; - - addr_cells = prom_n_addr_cells(phb); - if (addr_cells == 1) { - buid = (unsigned long) buid_vals[0]; - } else { - buid = (((unsigned long)buid_vals[0]) << 32UL) | - (((unsigned long)buid_vals[1]) & 0xffffffff); - } - return buid; -} - -static int phb_set_bus_ranges(struct device_node *dev, - struct pci_controller *phb) -{ - int *bus_range; - unsigned int len; - - bus_range = (int *) get_property(dev, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - return 1; - } - - phb->first_busno = bus_range[0]; - phb->last_busno = bus_range[1]; - - return 0; -} - -static int __devinit setup_phb(struct device_node *dev, - struct pci_controller *phb, - unsigned int addr_size_words) -{ - pci_setup_pci_controller(phb); - - if (is_python(dev)) - python_countermeasures(dev, addr_size_words); - - if (phb_set_bus_ranges(dev, phb)) - return 1; - - phb->arch_data = dev; - phb->ops = &rtas_pci_ops; - phb->buid = get_phb_buid(dev); - - return 0; -} - -static void __devinit add_linux_pci_domain(struct device_node *dev, - struct pci_controller *phb, - struct property *of_prop) -{ - memset(of_prop, 0, sizeof(struct property)); - of_prop->name = "linux,pci-domain"; - of_prop->length = sizeof(phb->global_number); - of_prop->value = (unsigned char *)&of_prop[1]; - memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); - prom_add_property(dev, of_prop); -} - -static struct pci_controller * __init alloc_phb(struct device_node *dev, - unsigned int addr_size_words) -{ - struct pci_controller *phb; - struct property *of_prop; - - phb = alloc_bootmem(sizeof(struct pci_controller)); - if (phb == NULL) - return NULL; - - of_prop = alloc_bootmem(sizeof(struct property) + - sizeof(phb->global_number)); - if (!of_prop) - return NULL; - - if (setup_phb(dev, phb, addr_size_words)) - return NULL; - - add_linux_pci_domain(dev, phb, of_prop); - - return phb; -} - -static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words) -{ - struct pci_controller *phb; - - phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), - GFP_KERNEL); - if (phb == NULL) - return NULL; - - if (setup_phb(dev, phb, addr_size_words)) - return NULL; - - phb->is_dynamic = 1; - - /* TODO: linux,pci-domain? */ - - return phb; -} - -unsigned long __init find_and_init_phbs(void) -{ - struct device_node *node; - struct pci_controller *phb; - unsigned int root_size_cells = 0; - unsigned int index; - unsigned int *opprop = NULL; - struct device_node *root = of_find_node_by_path("/"); - - if (ppc64_interrupt_controller == IC_OPEN_PIC) { - opprop = (unsigned int *)get_property(root, - "platform-open-pic", NULL); - } - - root_size_cells = prom_n_size_cells(root); - - index = 0; - - for (node = of_get_next_child(root, NULL); - node != NULL; - node = of_get_next_child(root, node)) { - if (node->type == NULL || strcmp(node->type, "pci") != 0) - continue; - - phb = alloc_phb(node, root_size_cells); - if (!phb) - continue; - - pci_process_bridge_OF_ranges(phb, node); - pci_setup_phb_io(phb, index == 0); - - if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { - int addr = root_size_cells * (index + 2) - 1; - mpic_assign_isu(pSeries_mpic, index, opprop[addr]); - } - - index++; - } - - of_node_put(root); - pci_devs_phb_init(); - - /* - * pci_probe_only and pci_assign_all_buses can be set via properties - * in chosen. - */ - if (of_chosen) { - int *prop; - - prop = (int *)get_property(of_chosen, "linux,pci-probe-only", - NULL); - if (prop) - pci_probe_only = *prop; - - prop = (int *)get_property(of_chosen, - "linux,pci-assign-all-buses", NULL); - if (prop) - pci_assign_all_buses = *prop; - } - - return 0; -} - -struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) -{ - struct device_node *root = of_find_node_by_path("/"); - unsigned int root_size_cells = 0; - struct pci_controller *phb; - struct pci_bus *bus; - int primary; - - root_size_cells = prom_n_size_cells(root); - - primary = list_empty(&hose_list); - phb = alloc_phb_dynamic(dn, root_size_cells); - if (!phb) - return NULL; - - pci_process_bridge_OF_ranges(phb, dn); - - pci_setup_phb_io_dynamic(phb, primary); - of_node_put(root); - - pci_devs_phb_init_dynamic(phb); - phb->last_busno = 0xff; - bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data); - phb->bus = bus; - phb->last_busno = bus->subordinate; - - return phb; -} -EXPORT_SYMBOL(init_phb_dynamic); +static int __initdata s7a_workaround = -1; #if 0 void pcibios_name_device(struct pci_dev *dev) @@ -474,11 +60,12 @@ void pcibios_name_device(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device); #endif -static void check_s7a(void) +static void __init check_s7a(void) { struct device_node *root; char *model; + s7a_workaround = 0; root = of_find_node_by_path("/"); if (root) { model = get_property(root, "model", NULL); @@ -488,55 +75,23 @@ static void check_s7a(void) } } -/* RPA-specific bits for removing PHBs */ -int pcibios_remove_root_bus(struct pci_controller *phb) +void __devinit pSeries_irq_bus_setup(struct pci_bus *bus) { - struct pci_bus *b = phb->bus; - struct resource *res; - int rc, i; + struct pci_dev *dev; - res = b->resource[0]; - if (!res->flags) { - printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__, - b->name); - return 1; - } - - rc = unmap_bus_range(b); - if (rc) { - printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", - __FUNCTION__, b->name); - return 1; - } - - if (release_resource(res)) { - printk(KERN_ERR "%s: failed to release IO on bus %s\n", - __FUNCTION__, b->name); - return 1; - } - - for (i = 1; i < 3; ++i) { - res = b->resource[i]; - if (!res->flags && i == 0) { - printk(KERN_ERR "%s: no MEM resource for PHB %s\n", - __FUNCTION__, b->name); - return 1; - } - if (res->flags && release_resource(res)) { - printk(KERN_ERR - "%s: failed to release IO %d on bus %s\n", - __FUNCTION__, i, b->name); - return 1; + if (s7a_workaround < 0) + check_s7a(); + list_for_each_entry(dev, &bus->devices, bus_list) { + pci_read_irq_line(dev); + if (s7a_workaround) { + if (dev->irq > 16) { + dev->irq -= 3; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, + dev->irq); + } } } - - list_del(&phb->list_node); - if (phb->is_dynamic) - kfree(phb); - - return 0; } -EXPORT_SYMBOL(pcibios_remove_root_bus); static void __init pSeries_request_regions(void) { @@ -553,20 +108,6 @@ static void __init pSeries_request_regions(void) void __init pSeries_final_fixup(void) { - struct pci_dev *dev = NULL; - - check_s7a(); - - for_each_pci_dev(dev) { - pci_read_irq_line(dev); - if (s7a_workaround) { - if (dev->irq > 16) { - dev->irq -= 3; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } - } - } - phbs_remap_io(); pSeries_request_regions(); diff --git a/arch/ppc64/kernel/pSeries_setup.c b/arch/ppc64/kernel/pSeries_setup.c index 6c0d1d58a552..f2b41243342c 100644 --- a/arch/ppc64/kernel/pSeries_setup.c +++ b/arch/ppc64/kernel/pSeries_setup.c @@ -71,11 +71,6 @@ #define DBG(fmt...) #endif -extern void pSeries_final_fixup(void); - -extern void pSeries_get_boot_time(struct rtc_time *rtc_time); -extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); -extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); extern void find_udbg_vterm(void); extern void system_reset_fwnmi(void); /* from head.S */ extern void machine_check_fwnmi(void); /* from head.S */ @@ -84,9 +79,6 @@ extern void generic_find_legacy_serial_ports(u64 *physport, int fwnmi_active; /* TRUE if an FWNMI handler is present */ -extern unsigned long ppc_proc_freq; -extern unsigned long ppc_tb_freq; - extern void pSeries_system_reset_exception(struct pt_regs *regs); extern int pSeries_machine_check_exception(struct pt_regs *regs); @@ -381,171 +373,6 @@ static void __init pSeries_init_early(void) } -static void pSeries_progress(char *s, unsigned short hex) -{ - struct device_node *root; - int width, *p; - char *os; - static int display_character, set_indicator; - static int max_width; - static DEFINE_SPINLOCK(progress_lock); - static int pending_newline = 0; /* did last write end with unprinted newline? */ - - if (!rtas.base) - return; - - 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; - display_character = rtas_token("display-character"); - set_indicator = rtas_token("set-indicator"); - } - - if (display_character == RTAS_UNKNOWN_SERVICE) { - /* use hex display if available */ - if (set_indicator != RTAS_UNKNOWN_SERVICE) - rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); - return; - } - - spin_lock(&progress_lock); - - /* - * Last write ended with newline, but we didn't print it since - * it would just clear the bottom line of output. Print it now - * instead. - * - * If no newline is pending, print a CR to start output at the - * beginning of the line. - */ - if (pending_newline) { - rtas_call(display_character, 1, 1, NULL, '\r'); - rtas_call(display_character, 1, 1, NULL, '\n'); - pending_newline = 0; - } else { - rtas_call(display_character, 1, 1, NULL, '\r'); - } - - width = max_width; - os = s; - while (*os) { - if (*os == '\n' || *os == '\r') { - /* Blank to end of line. */ - while (width-- > 0) - rtas_call(display_character, 1, 1, NULL, ' '); - - /* If newline is the last character, save it - * until next call to avoid bumping up the - * display output. - */ - if (*os == '\n' && !os[1]) { - pending_newline = 1; - spin_unlock(&progress_lock); - return; - } - - /* RTAS wants CR-LF, not just LF */ - - if (*os == '\n') { - rtas_call(display_character, 1, 1, NULL, '\r'); - rtas_call(display_character, 1, 1, NULL, '\n'); - } else { - /* CR might be used to re-draw a line, so we'll - * leave it alone and not add LF. - */ - rtas_call(display_character, 1, 1, NULL, *os); - } - - width = max_width; - } else { - width--; - rtas_call(display_character, 1, 1, NULL, *os); - } - - 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) - rtas_call(display_character, 1, 1, NULL, ' '); - - spin_unlock(&progress_lock); -} - -extern void setup_default_decr(void); - -/* Some sane defaults: 125 MHz timebase, 1GHz processor */ -#define DEFAULT_TB_FREQ 125000000UL -#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) - -static void __init pSeries_calibrate_decr(void) -{ - struct device_node *cpu; - struct div_result divres; - unsigned int *fp; - int node_found; - - /* - * The cpu node should have a timebase-frequency property - * to tell us the rate at which the decrementer counts. - */ - cpu = of_find_node_by_type(NULL, "cpu"); - - ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */ - node_found = 0; - if (cpu != 0) { - fp = (unsigned int *)get_property(cpu, "timebase-frequency", - NULL); - if (fp != 0) { - node_found = 1; - ppc_tb_freq = *fp; - } - } - if (!node_found) - printk(KERN_ERR "WARNING: Estimating decrementer frequency " - "(not found)\n"); - - ppc_proc_freq = DEFAULT_PROC_FREQ; - node_found = 0; - if (cpu != 0) { - fp = (unsigned int *)get_property(cpu, "clock-frequency", - NULL); - if (fp != 0) { - node_found = 1; - ppc_proc_freq = *fp; - } - } - if (!node_found) - printk(KERN_ERR "WARNING: Estimating processor frequency " - "(not found)\n"); - - of_node_put(cpu); - - printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", - ppc_tb_freq/1000000, ppc_tb_freq%1000000); - printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", - ppc_proc_freq/1000000, ppc_proc_freq%1000000); - - tb_ticks_per_jiffy = ppc_tb_freq / HZ; - tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; - tb_ticks_per_usec = ppc_tb_freq / 1000000; - tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); - div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); - tb_to_xs = divres.result_low; - - setup_default_decr(); -} - static int pSeries_check_legacy_ioport(unsigned int baseport) { struct device_node *np; @@ -596,16 +423,17 @@ struct machdep_calls __initdata pSeries_md = { .get_cpuinfo = pSeries_get_cpuinfo, .log_error = pSeries_log_error, .pcibios_fixup = pSeries_final_fixup, + .irq_bus_setup = pSeries_irq_bus_setup, .restart = rtas_restart, .power_off = rtas_power_off, .halt = rtas_halt, .panic = rtas_os_term, .cpu_die = pSeries_mach_cpu_die, - .get_boot_time = pSeries_get_boot_time, - .get_rtc_time = pSeries_get_rtc_time, - .set_rtc_time = pSeries_set_rtc_time, - .calibrate_decr = pSeries_calibrate_decr, - .progress = pSeries_progress, + .get_boot_time = rtas_get_boot_time, + .get_rtc_time = rtas_get_rtc_time, + .set_rtc_time = rtas_set_rtc_time, + .calibrate_decr = generic_calibrate_decr, + .progress = rtas_progress, .check_legacy_ioport = pSeries_check_legacy_ioport, .system_reset_exception = pSeries_system_reset_exception, .machine_check_exception = pSeries_machine_check_exception, diff --git a/arch/ppc64/kernel/pSeries_smp.c b/arch/ppc64/kernel/pSeries_smp.c index 4203bd020c82..30154140f7e2 100644 --- a/arch/ppc64/kernel/pSeries_smp.c +++ b/arch/ppc64/kernel/pSeries_smp.c @@ -1,5 +1,5 @@ /* - * SMP support for pSeries machines. + * SMP support for pSeries and BPA machines. * * Dave Engebretsen, Peter Bergner, and * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com @@ -47,6 +47,7 @@ #include #include "mpic.h" +#include "bpa_iic.h" #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -286,6 +287,7 @@ static inline int __devinit smp_startup_cpu(unsigned int lcpu) return 1; } +#ifdef CONFIG_XICS static inline void smp_xics_do_message(int cpu, int msg) { set_bit(msg, &xics_ipi_message[cpu].value); @@ -327,6 +329,37 @@ static void __devinit smp_xics_setup_cpu(int cpu) cpu_clear(cpu, of_spin_map); } +#endif /* CONFIG_XICS */ +#ifdef CONFIG_BPA_IIC +static void smp_iic_message_pass(int target, int msg) +{ + unsigned int i; + + if (target < NR_CPUS) { + iic_cause_IPI(target, msg); + } else { + for_each_online_cpu(i) { + if (target == MSG_ALL_BUT_SELF + && i == smp_processor_id()) + continue; + iic_cause_IPI(i, msg); + } + } +} + +static int __init smp_iic_probe(void) +{ + iic_request_IPIs(); + + return cpus_weight(cpu_possible_map); +} + +static void __devinit smp_iic_setup_cpu(int cpu) +{ + if (cpu != boot_cpuid) + iic_setup_cpu(); +} +#endif /* CONFIG_BPA_IIC */ static DEFINE_SPINLOCK(timebase_lock); static unsigned long timebase = 0; @@ -381,14 +414,15 @@ static int smp_pSeries_cpu_bootable(unsigned int nr) return 1; } - +#ifdef CONFIG_MPIC static struct smp_ops_t pSeries_mpic_smp_ops = { .message_pass = smp_mpic_message_pass, .probe = smp_mpic_probe, .kick_cpu = smp_pSeries_kick_cpu, .setup_cpu = smp_mpic_setup_cpu, }; - +#endif +#ifdef CONFIG_XICS static struct smp_ops_t pSeries_xics_smp_ops = { .message_pass = smp_xics_message_pass, .probe = smp_xics_probe, @@ -396,6 +430,16 @@ static struct smp_ops_t pSeries_xics_smp_ops = { .setup_cpu = smp_xics_setup_cpu, .cpu_bootable = smp_pSeries_cpu_bootable, }; +#endif +#ifdef CONFIG_BPA_IIC +static struct smp_ops_t bpa_iic_smp_ops = { + .message_pass = smp_iic_message_pass, + .probe = smp_iic_probe, + .kick_cpu = smp_pSeries_kick_cpu, + .setup_cpu = smp_iic_setup_cpu, + .cpu_bootable = smp_pSeries_cpu_bootable, +}; +#endif /* This is called very early */ void __init smp_init_pSeries(void) @@ -404,10 +448,25 @@ void __init smp_init_pSeries(void) DBG(" -> smp_init_pSeries()\n"); - if (ppc64_interrupt_controller == IC_OPEN_PIC) + switch (ppc64_interrupt_controller) { +#ifdef CONFIG_MPIC + case IC_OPEN_PIC: smp_ops = &pSeries_mpic_smp_ops; - else + break; +#endif +#ifdef CONFIG_XICS + case IC_PPC_XIC: smp_ops = &pSeries_xics_smp_ops; + break; +#endif +#ifdef CONFIG_BPA_IIC + case IC_BPA_IIC: + smp_ops = &bpa_iic_smp_ops; + break; +#endif + default: + panic("Invalid interrupt controller"); + } #ifdef CONFIG_HOTPLUG_CPU smp_ops->cpu_disable = pSeries_cpu_disable; diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index 2bf0513f3eca..580676f87d23 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -902,6 +902,9 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) ppc_md.iommu_dev_setup(dev); + if (ppc_md.irq_bus_setup) + ppc_md.irq_bus_setup(bus); + if (!pci_probe_only) return; diff --git a/arch/ppc64/kernel/pci.h b/arch/ppc64/kernel/pci.h index 0fd7d849aa77..26be78b13af1 100644 --- a/arch/ppc64/kernel/pci.h +++ b/arch/ppc64/kernel/pci.h @@ -40,10 +40,14 @@ struct device_node *fetch_dev_dn(struct pci_dev *dev); void pci_addr_cache_insert_device(struct pci_dev *dev); void pci_addr_cache_remove_device(struct pci_dev *dev); -/* From pSeries_pci.h */ +/* From rtas_pci.h */ void init_pci_config_tokens (void); unsigned long get_phb_buid (struct device_node *); +/* From pSeries_pci.h */ +extern void pSeries_final_fixup(void); +extern void pSeries_irq_bus_setup(struct pci_bus *bus); + extern unsigned long pci_probe_only; extern unsigned long pci_assign_all_buses; extern int pci_read_irq_line(struct pci_dev *pci_dev); diff --git a/arch/ppc64/kernel/pmac_time.c b/arch/ppc64/kernel/pmac_time.c index f24827581dd7..3059edb09cc8 100644 --- a/arch/ppc64/kernel/pmac_time.c +++ b/arch/ppc64/kernel/pmac_time.c @@ -40,11 +40,6 @@ #define DBG(x...) #endif -extern void setup_default_decr(void); - -extern unsigned long ppc_tb_freq; -extern unsigned long ppc_proc_freq; - /* Apparently the RTC stores seconds since 1 Jan 1904 */ #define RTC_OFFSET 2082844800 @@ -161,8 +156,7 @@ void __init pmac_get_boot_time(struct rtc_time *tm) /* * Query the OF and get the decr frequency. - * This was taken from the pmac time_init() when merging the prep/pmac - * time functions. + * FIXME: merge this with generic_calibrate_decr */ void __init pmac_calibrate_decr(void) { diff --git a/arch/ppc64/kernel/proc_ppc64.c b/arch/ppc64/kernel/proc_ppc64.c index 0914b0669b05..a87c66a9652a 100644 --- a/arch/ppc64/kernel/proc_ppc64.c +++ b/arch/ppc64/kernel/proc_ppc64.c @@ -53,7 +53,7 @@ static int __init proc_ppc64_create(void) if (!root) return 1; - if (!(systemcfg->platform & PLATFORM_PSERIES)) + if (!(systemcfg->platform & (PLATFORM_PSERIES | PLATFORM_BPA))) return 0; if (!proc_mkdir("rtas", root)) diff --git a/arch/ppc64/kernel/prom_init.c b/arch/ppc64/kernel/prom_init.c index b7683abfbe6a..e248a7950aeb 100644 --- a/arch/ppc64/kernel/prom_init.c +++ b/arch/ppc64/kernel/prom_init.c @@ -1915,9 +1915,9 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, unsigned long prom_send_capabilities(); /* - * On pSeries, copy the CPU hold code + * On pSeries and BPA, copy the CPU hold code */ - if (RELOC(of_platform) & PLATFORM_PSERIES) + if (RELOC(of_platform) & (PLATFORM_PSERIES | PLATFORM_BPA)) copy_and_flush(0, KERNELBASE - offset, 0x100, 0); /* diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c index 9f8c6087ae56..2993f108d96d 100644 --- a/arch/ppc64/kernel/ptrace.c +++ b/arch/ppc64/kernel/ptrace.c @@ -305,6 +305,8 @@ static void do_syscall_trace(void) void do_syscall_trace_enter(struct pt_regs *regs) { + secure_computing(regs->gpr[0]); + if (test_thread_flag(TIF_SYSCALL_TRACE) && (current->ptrace & PT_PTRACED)) do_syscall_trace(); @@ -320,8 +322,6 @@ void do_syscall_trace_enter(struct pt_regs *regs) void do_syscall_trace_leave(struct pt_regs *regs) { - secure_computing(regs->gpr[0]); - if (unlikely(current->audit_context)) audit_syscall_exit(current, (regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, diff --git a/arch/ppc64/kernel/rtas-proc.c b/arch/ppc64/kernel/rtas-proc.c index 28b1f1521f21..1f3ff860fdf0 100644 --- a/arch/ppc64/kernel/rtas-proc.c +++ b/arch/ppc64/kernel/rtas-proc.c @@ -371,11 +371,11 @@ static ssize_t ppc_rtas_progress_write(struct file *file, /* Lets see if the user passed hexdigits */ hex = simple_strtoul(progress_led, NULL, 10); - ppc_md.progress ((char *)progress_led, hex); + rtas_progress ((char *)progress_led, hex); return count; /* clear the line */ - /* ppc_md.progress(" ", 0xffff);*/ + /* rtas_progress(" ", 0xffff);*/ } /* ****************************************************************** */ static int ppc_rtas_progress_show(struct seq_file *m, void *v) diff --git a/arch/ppc64/kernel/rtas.c b/arch/ppc64/kernel/rtas.c index 5575603def27..5e8eb33b8e54 100644 --- a/arch/ppc64/kernel/rtas.c +++ b/arch/ppc64/kernel/rtas.c @@ -91,6 +91,123 @@ call_rtas_display_status_delay(unsigned char c) } } +void +rtas_progress(char *s, unsigned short hex) +{ + struct device_node *root; + int width, *p; + char *os; + static int display_character, set_indicator; + static int display_width, display_lines, *row_width, form_feed; + static DEFINE_SPINLOCK(progress_lock); + static int current_line; + static int pending_newline = 0; /* did last write end with unprinted newline? */ + + if (!rtas.base) + return; + + if (display_width == 0) { + display_width = 0x10; + if ((root = find_path_device("/rtas"))) { + if ((p = (unsigned int *)get_property(root, + "ibm,display-line-length", NULL))) + display_width = *p; + if ((p = (unsigned int *)get_property(root, + "ibm,form-feed", NULL))) + form_feed = *p; + if ((p = (unsigned int *)get_property(root, + "ibm,display-number-of-lines", NULL))) + display_lines = *p; + row_width = (unsigned int *)get_property(root, + "ibm,display-truncation-length", NULL); + } + display_character = rtas_token("display-character"); + set_indicator = rtas_token("set-indicator"); + } + + if (display_character == RTAS_UNKNOWN_SERVICE) { + /* use hex display if available */ + if (set_indicator != RTAS_UNKNOWN_SERVICE) + rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); + return; + } + + spin_lock(&progress_lock); + + /* + * Last write ended with newline, but we didn't print it since + * it would just clear the bottom line of output. Print it now + * instead. + * + * If no newline is pending and form feed is supported, clear the + * display with a form feed; otherwise, print a CR to start output + * at the beginning of the line. + */ + if (pending_newline) { + rtas_call(display_character, 1, 1, NULL, '\r'); + rtas_call(display_character, 1, 1, NULL, '\n'); + pending_newline = 0; + } else { + current_line = 0; + if (form_feed) + rtas_call(display_character, 1, 1, NULL, + (char)form_feed); + else + rtas_call(display_character, 1, 1, NULL, '\r'); + } + + if (row_width) + width = row_width[current_line]; + else + width = display_width; + os = s; + while (*os) { + if (*os == '\n' || *os == '\r') { + /* If newline is the last character, save it + * until next call to avoid bumping up the + * display output. + */ + if (*os == '\n' && !os[1]) { + pending_newline = 1; + current_line++; + if (current_line > display_lines-1) + current_line = display_lines-1; + spin_unlock(&progress_lock); + return; + } + + /* RTAS wants CR-LF, not just LF */ + + if (*os == '\n') { + rtas_call(display_character, 1, 1, NULL, '\r'); + rtas_call(display_character, 1, 1, NULL, '\n'); + } else { + /* CR might be used to re-draw a line, so we'll + * leave it alone and not add LF. + */ + rtas_call(display_character, 1, 1, NULL, *os); + } + + if (row_width) + width = row_width[current_line]; + else + width = display_width; + } else { + width--; + rtas_call(display_character, 1, 1, NULL, *os); + } + + os++; + + /* if we overwrite the screen length */ + if (width <= 0) + while ((*os != 0) && (*os != '\n') && (*os != '\r')) + os++; + } + + spin_unlock(&progress_lock); +} + int rtas_token(const char *service) { @@ -425,8 +542,8 @@ rtas_flash_firmware(void) printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size); printk(KERN_ALERT "FLASH: performing flash and reboot\n"); - ppc_md.progress("Flashing \n", 0x0); - ppc_md.progress("Please Wait... ", 0x0); + rtas_progress("Flashing \n", 0x0); + rtas_progress("Please Wait... ", 0x0); printk(KERN_ALERT "FLASH: this will take several minutes. Do not power off!\n"); status = rtas_call(update_token, 1, 1, NULL, rtas_block_list); switch (status) { /* should only get "bad" status */ diff --git a/arch/ppc64/kernel/rtas_pci.c b/arch/ppc64/kernel/rtas_pci.c new file mode 100644 index 000000000000..1048817befb8 --- /dev/null +++ b/arch/ppc64/kernel/rtas_pci.c @@ -0,0 +1,495 @@ +/* + * arch/ppc64/kernel/rtas_pci.c + * + * Copyright (C) 2001 Dave Engebretsen, IBM Corporation + * Copyright (C) 2003 Anton Blanchard , IBM + * + * RTAS specific routines for PCI. + * + * Based on code from pci.c, chrp_pci.c and pSeries_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 "mpic.h" +#include "pci.h" + +/* RTAS tokens */ +static int read_pci_config; +static int write_pci_config; +static int ibm_read_pci_config; +static int ibm_write_pci_config; + +static int config_access_valid(struct device_node *dn, int where) +{ + if (where < 256) + return 1; + if (where < 4096 && dn->pci_ext_config_space) + return 1; + + return 0; +} + +static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val) +{ + int returnval = -1; + unsigned long buid, addr; + int ret; + + if (!dn) + return PCIBIOS_DEVICE_NOT_FOUND; + if (!config_access_valid(dn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + addr = ((where & 0xf00) << 20) | (dn->busno << 16) | + (dn->devfn << 8) | (where & 0xff); + buid = dn->phb->buid; + if (buid) { + ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, + addr, buid >> 32, buid & 0xffffffff, size); + } else { + ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); + } + *val = returnval; + + if (ret) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (returnval == EEH_IO_ERROR_VALUE(size) + && eeh_dn_check_failure (dn, NULL)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int rtas_pci_read_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 *val) +{ + struct device_node *busdn, *dn; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + + /* Search only direct children of the bus */ + for (dn = busdn->child; dn; dn = dn->sibling) + if (dn->devfn == devfn) + return rtas_read_config(dn, where, size, val); + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static int rtas_write_config(struct device_node *dn, int where, int size, u32 val) +{ + unsigned long buid, addr; + int ret; + + if (!dn) + return PCIBIOS_DEVICE_NOT_FOUND; + if (!config_access_valid(dn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + addr = ((where & 0xf00) << 20) | (dn->busno << 16) | + (dn->devfn << 8) | (where & 0xff); + buid = dn->phb->buid; + if (buid) { + ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val); + } else { + ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); + } + + if (ret) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int rtas_pci_write_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 val) +{ + struct device_node *busdn, *dn; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + + /* Search only direct children of the bus */ + for (dn = busdn->child; dn; dn = dn->sibling) + if (dn->devfn == devfn) + return rtas_write_config(dn, where, size, val); + return PCIBIOS_DEVICE_NOT_FOUND; +} + +struct pci_ops rtas_pci_ops = { + rtas_pci_read_config, + rtas_pci_write_config +}; + +int is_python(struct device_node *dev) +{ + char *model = (char *)get_property(dev, "model", NULL); + + if (model && strstr(model, "Python")) + return 1; + + return 0; +} + +static int get_phb_reg_prop(struct device_node *dev, + unsigned int addr_size_words, + struct reg_property64 *reg) +{ + unsigned int *ui_ptr = NULL, len; + + /* Found a PHB, now figure out where his registers are mapped. */ + ui_ptr = (unsigned int *)get_property(dev, "reg", &len); + if (ui_ptr == NULL) + return 1; + + if (addr_size_words == 1) { + reg->address = ((struct reg_property32 *)ui_ptr)->address; + reg->size = ((struct reg_property32 *)ui_ptr)->size; + } else { + *reg = *((struct reg_property64 *)ui_ptr); + } + + return 0; +} + +static void python_countermeasures(struct device_node *dev, + unsigned int addr_size_words) +{ + struct reg_property64 reg_struct; + void __iomem *chip_regs; + volatile u32 val; + + if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) + return; + + /* Python's register file is 1 MB in size. */ + chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); + + /* + * Firmware doesn't always clear this bit which is critical + * for good performance - Anton + */ + +#define PRG_CL_RESET_VALID 0x00010000 + + val = in_be32(chip_regs + 0xf6030); + if (val & PRG_CL_RESET_VALID) { + printk(KERN_INFO "Python workaround: "); + val &= ~PRG_CL_RESET_VALID; + out_be32(chip_regs + 0xf6030, val); + /* + * We must read it back for changes to + * take effect + */ + val = in_be32(chip_regs + 0xf6030); + printk("reg0: %x\n", val); + } + + iounmap(chip_regs); +} + +void __init init_pci_config_tokens (void) +{ + read_pci_config = rtas_token("read-pci-config"); + write_pci_config = rtas_token("write-pci-config"); + ibm_read_pci_config = rtas_token("ibm,read-pci-config"); + ibm_write_pci_config = rtas_token("ibm,write-pci-config"); +} + +unsigned long __devinit get_phb_buid (struct device_node *phb) +{ + int addr_cells; + unsigned int *buid_vals; + unsigned int len; + unsigned long buid; + + if (ibm_read_pci_config == -1) return 0; + + /* PHB's will always be children of the root node, + * or so it is promised by the current firmware. */ + if (phb->parent == NULL) + return 0; + if (phb->parent->parent) + return 0; + + buid_vals = (unsigned int *) get_property(phb, "reg", &len); + if (buid_vals == NULL) + return 0; + + addr_cells = prom_n_addr_cells(phb); + if (addr_cells == 1) { + buid = (unsigned long) buid_vals[0]; + } else { + buid = (((unsigned long)buid_vals[0]) << 32UL) | + (((unsigned long)buid_vals[1]) & 0xffffffff); + } + return buid; +} + +static int phb_set_bus_ranges(struct device_node *dev, + struct pci_controller *phb) +{ + int *bus_range; + unsigned int len; + + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + return 1; + } + + phb->first_busno = bus_range[0]; + phb->last_busno = bus_range[1]; + + return 0; +} + +static int __devinit setup_phb(struct device_node *dev, + struct pci_controller *phb, + unsigned int addr_size_words) +{ + pci_setup_pci_controller(phb); + + if (is_python(dev)) + python_countermeasures(dev, addr_size_words); + + if (phb_set_bus_ranges(dev, phb)) + return 1; + + phb->arch_data = dev; + phb->ops = &rtas_pci_ops; + phb->buid = get_phb_buid(dev); + + return 0; +} + +static void __devinit add_linux_pci_domain(struct device_node *dev, + struct pci_controller *phb, + struct property *of_prop) +{ + memset(of_prop, 0, sizeof(struct property)); + of_prop->name = "linux,pci-domain"; + of_prop->length = sizeof(phb->global_number); + of_prop->value = (unsigned char *)&of_prop[1]; + memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); + prom_add_property(dev, of_prop); +} + +static struct pci_controller * __init alloc_phb(struct device_node *dev, + unsigned int addr_size_words) +{ + struct pci_controller *phb; + struct property *of_prop; + + phb = alloc_bootmem(sizeof(struct pci_controller)); + if (phb == NULL) + return NULL; + + of_prop = alloc_bootmem(sizeof(struct property) + + sizeof(phb->global_number)); + if (!of_prop) + return NULL; + + if (setup_phb(dev, phb, addr_size_words)) + return NULL; + + add_linux_pci_domain(dev, phb, of_prop); + + return phb; +} + +static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words) +{ + struct pci_controller *phb; + + phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), + GFP_KERNEL); + if (phb == NULL) + return NULL; + + if (setup_phb(dev, phb, addr_size_words)) + return NULL; + + phb->is_dynamic = 1; + + /* TODO: linux,pci-domain? */ + + return phb; +} + +unsigned long __init find_and_init_phbs(void) +{ + struct device_node *node; + struct pci_controller *phb; + unsigned int root_size_cells = 0; + unsigned int index; + unsigned int *opprop = NULL; + struct device_node *root = of_find_node_by_path("/"); + + if (ppc64_interrupt_controller == IC_OPEN_PIC) { + opprop = (unsigned int *)get_property(root, + "platform-open-pic", NULL); + } + + root_size_cells = prom_n_size_cells(root); + + index = 0; + + for (node = of_get_next_child(root, NULL); + node != NULL; + node = of_get_next_child(root, node)) { + if (node->type == NULL || strcmp(node->type, "pci") != 0) + continue; + + phb = alloc_phb(node, root_size_cells); + if (!phb) + continue; + + pci_process_bridge_OF_ranges(phb, node); + pci_setup_phb_io(phb, index == 0); +#ifdef CONFIG_PPC_PSERIES + if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { + int addr = root_size_cells * (index + 2) - 1; + mpic_assign_isu(pSeries_mpic, index, opprop[addr]); + } +#endif + index++; + } + + of_node_put(root); + pci_devs_phb_init(); + + /* + * pci_probe_only and pci_assign_all_buses can be set via properties + * in chosen. + */ + if (of_chosen) { + int *prop; + + prop = (int *)get_property(of_chosen, "linux,pci-probe-only", + NULL); + if (prop) + pci_probe_only = *prop; + + prop = (int *)get_property(of_chosen, + "linux,pci-assign-all-buses", NULL); + if (prop) + pci_assign_all_buses = *prop; + } + + return 0; +} + +struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) +{ + struct device_node *root = of_find_node_by_path("/"); + unsigned int root_size_cells = 0; + struct pci_controller *phb; + struct pci_bus *bus; + int primary; + + root_size_cells = prom_n_size_cells(root); + + primary = list_empty(&hose_list); + phb = alloc_phb_dynamic(dn, root_size_cells); + if (!phb) + return NULL; + + pci_process_bridge_OF_ranges(phb, dn); + + pci_setup_phb_io_dynamic(phb, primary); + of_node_put(root); + + pci_devs_phb_init_dynamic(phb); + phb->last_busno = 0xff; + bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data); + phb->bus = bus; + phb->last_busno = bus->subordinate; + + return phb; +} +EXPORT_SYMBOL(init_phb_dynamic); + +/* RPA-specific bits for removing PHBs */ +int pcibios_remove_root_bus(struct pci_controller *phb) +{ + struct pci_bus *b = phb->bus; + struct resource *res; + int rc, i; + + res = b->resource[0]; + if (!res->flags) { + printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__, + b->name); + return 1; + } + + rc = unmap_bus_range(b); + if (rc) { + printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", + __FUNCTION__, b->name); + return 1; + } + + if (release_resource(res)) { + printk(KERN_ERR "%s: failed to release IO on bus %s\n", + __FUNCTION__, b->name); + return 1; + } + + for (i = 1; i < 3; ++i) { + res = b->resource[i]; + if (!res->flags && i == 0) { + printk(KERN_ERR "%s: no MEM resource for PHB %s\n", + __FUNCTION__, b->name); + return 1; + } + if (res->flags && release_resource(res)) { + printk(KERN_ERR + "%s: failed to release IO %d on bus %s\n", + __FUNCTION__, i, b->name); + return 1; + } + } + + list_del(&phb->list_node); + if (phb->is_dynamic) + kfree(phb); + + return 0; +} +EXPORT_SYMBOL(pcibios_remove_root_bus); diff --git a/arch/ppc64/kernel/rtc.c b/arch/ppc64/kernel/rtc.c index de02aedbe080..d729fefa0df5 100644 --- a/arch/ppc64/kernel/rtc.c +++ b/arch/ppc64/kernel/rtc.c @@ -301,7 +301,7 @@ void iSeries_get_boot_time(struct rtc_time *tm) #ifdef CONFIG_PPC_RTAS #define MAX_RTC_WAIT 5000 /* 5 sec */ #define RTAS_CLOCK_BUSY (-2) -void pSeries_get_boot_time(struct rtc_time *rtc_tm) +void rtas_get_boot_time(struct rtc_time *rtc_tm) { int ret[8]; int error, wait_time; @@ -336,7 +336,7 @@ void pSeries_get_boot_time(struct rtc_time *rtc_tm) * and if a delay is needed to read the clock. In this case we just * silently return without updating rtc_tm. */ -void pSeries_get_rtc_time(struct rtc_time *rtc_tm) +void rtas_get_rtc_time(struct rtc_time *rtc_tm) { int ret[8]; int error, wait_time; @@ -371,7 +371,7 @@ void pSeries_get_rtc_time(struct rtc_time *rtc_tm) rtc_tm->tm_year = ret[0] - 1900; } -int pSeries_set_rtc_time(struct rtc_time *tm) +int rtas_set_rtc_time(struct rtc_time *tm) { int error, wait_time; unsigned long max_wait_tb; diff --git a/arch/ppc64/kernel/setup.c b/arch/ppc64/kernel/setup.c index 8e439a817642..0a47a5ef428d 100644 --- a/arch/ppc64/kernel/setup.c +++ b/arch/ppc64/kernel/setup.c @@ -344,6 +344,7 @@ static void __init setup_cpu_maps(void) extern struct machdep_calls pSeries_md; extern struct machdep_calls pmac_md; extern struct machdep_calls maple_md; +extern struct machdep_calls bpa_md; /* Ultimately, stuff them in an elf section like initcalls... */ static struct machdep_calls __initdata *machines[] = { @@ -356,6 +357,9 @@ static struct machdep_calls __initdata *machines[] = { #ifdef CONFIG_PPC_MAPLE &maple_md, #endif /* CONFIG_PPC_MAPLE */ +#ifdef CONFIG_PPC_BPA + &bpa_md, +#endif NULL }; @@ -679,6 +683,12 @@ void machine_restart(char *cmd) if (ppc_md.nvram_sync) ppc_md.nvram_sync(); ppc_md.restart(cmd); +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + local_irq_disable(); + while (1) ; } EXPORT_SYMBOL(machine_restart); @@ -688,6 +698,12 @@ void machine_power_off(void) if (ppc_md.nvram_sync) ppc_md.nvram_sync(); ppc_md.power_off(); +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + local_irq_disable(); + while (1) ; } EXPORT_SYMBOL(machine_power_off); @@ -697,13 +713,16 @@ void machine_halt(void) if (ppc_md.nvram_sync) ppc_md.nvram_sync(); ppc_md.halt(); +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + local_irq_disable(); + while (1) ; } EXPORT_SYMBOL(machine_halt); -unsigned long ppc_proc_freq; -unsigned long ppc_tb_freq; - static int ppc64_panic_event(struct notifier_block *this, unsigned long event, void *ptr) { @@ -1055,6 +1074,7 @@ void __init setup_arch(char **cmdline_p) /* set up the bootmem stuff with available memory */ do_init_bootmem(); + sparse_init(); /* initialize the syscall map in systemcfg */ setup_syscall_map(); @@ -1079,11 +1099,11 @@ void __init setup_arch(char **cmdline_p) static void ppc64_do_msg(unsigned int src, const char *msg) { if (ppc_md.progress) { - char buf[32]; + char buf[128]; - sprintf(buf, "%08x \n", src); + sprintf(buf, "%08X\n", src); ppc_md.progress(buf, 0); - sprintf(buf, "%-16s", msg); + snprintf(buf, 128, "%s", msg); ppc_md.progress(buf, 0); } } @@ -1117,7 +1137,7 @@ void ppc64_dump_msg(unsigned int src, const char *msg) } /* This should only be called on processor 0 during calibrate decr */ -void setup_default_decr(void) +void __init setup_default_decr(void) { struct paca_struct *lpaca = get_paca(); diff --git a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c index 9ef5d36d6b25..2fcddfcb594d 100644 --- a/arch/ppc64/kernel/smp.c +++ b/arch/ppc64/kernel/smp.c @@ -71,7 +71,7 @@ void smp_call_function_interrupt(void); int smt_enabled_at_boot = 1; -#ifdef CONFIG_PPC_MULTIPLATFORM +#ifdef CONFIG_MPIC void smp_mpic_message_pass(int target, int msg) { /* make sure we're sending something that translates to an IPI */ @@ -128,7 +128,7 @@ void __devinit smp_generic_kick_cpu(int nr) smp_mb(); } -#endif /* CONFIG_PPC_MULTIPLATFORM */ +#endif /* CONFIG_MPIC */ static void __init smp_space_timers(unsigned int max_cpus) { diff --git a/arch/ppc64/kernel/spider-pic.c b/arch/ppc64/kernel/spider-pic.c new file mode 100644 index 000000000000..d5c9a02fb119 --- /dev/null +++ b/arch/ppc64/kernel/spider-pic.c @@ -0,0 +1,191 @@ +/* + * External Interrupt Controller on Spider South Bridge + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include +#include +#include + +#include "bpa_iic.h" + +/* register layout taken from Spider spec, table 7.4-4 */ +enum { + TIR_DEN = 0x004, /* Detection Enable Register */ + TIR_MSK = 0x084, /* Mask Level Register */ + TIR_EDC = 0x0c0, /* Edge Detection Clear Register */ + TIR_PNDA = 0x100, /* Pending Register A */ + TIR_PNDB = 0x104, /* Pending Register B */ + TIR_CS = 0x144, /* Current Status Register */ + TIR_LCSA = 0x150, /* Level Current Status Register A */ + TIR_LCSB = 0x154, /* Level Current Status Register B */ + TIR_LCSC = 0x158, /* Level Current Status Register C */ + TIR_LCSD = 0x15c, /* Level Current Status Register D */ + TIR_CFGA = 0x200, /* Setting Register A0 */ + TIR_CFGB = 0x204, /* Setting Register B0 */ + /* 0x208 ... 0x3ff Setting Register An/Bn */ + TIR_PPNDA = 0x400, /* Packet Pending Register A */ + TIR_PPNDB = 0x404, /* Packet Pending Register B */ + TIR_PIERA = 0x408, /* Packet Output Error Register A */ + TIR_PIERB = 0x40c, /* Packet Output Error Register B */ + TIR_PIEN = 0x444, /* Packet Output Enable Register */ + TIR_PIPND = 0x454, /* Packet Output Pending Register */ + TIRDID = 0x484, /* Spider Device ID Register */ + REISTIM = 0x500, /* Reissue Command Timeout Time Setting */ + REISTIMEN = 0x504, /* Reissue Command Timeout Setting */ + REISWAITEN = 0x508, /* Reissue Wait Control*/ +}; + +static void __iomem *spider_pics[4]; + +static void __iomem *spider_get_pic(int irq) +{ + int node = irq / IIC_NODE_STRIDE; + irq %= IIC_NODE_STRIDE; + + if (irq >= IIC_EXT_OFFSET && + irq < IIC_EXT_OFFSET + IIC_NUM_EXT && + spider_pics) + return spider_pics[node]; + return NULL; +} + +static int spider_get_nr(unsigned int irq) +{ + return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET; +} + +static void __iomem *spider_get_irq_config(int irq) +{ + void __iomem *pic; + pic = spider_get_pic(irq); + return pic + TIR_CFGA + 8 * spider_get_nr(irq); +} + +static void spider_enable_irq(unsigned int irq) +{ + void __iomem *cfg = spider_get_irq_config(irq); + irq = spider_get_nr(irq); + + out_be32(cfg, in_be32(cfg) | 0x3107000eu); + out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq); +} + +static void spider_disable_irq(unsigned int irq) +{ + void __iomem *cfg = spider_get_irq_config(irq); + irq = spider_get_nr(irq); + + out_be32(cfg, in_be32(cfg) & ~0x30000000u); +} + +static unsigned int spider_startup_irq(unsigned int irq) +{ + spider_enable_irq(irq); + return 0; +} + +static void spider_shutdown_irq(unsigned int irq) +{ + spider_disable_irq(irq); +} + +static void spider_end_irq(unsigned int irq) +{ + spider_enable_irq(irq); +} + +static void spider_ack_irq(unsigned int irq) +{ + spider_disable_irq(irq); + iic_local_enable(); +} + +static struct hw_interrupt_type spider_pic = { + .typename = " SPIDER ", + .startup = spider_startup_irq, + .shutdown = spider_shutdown_irq, + .enable = spider_enable_irq, + .disable = spider_disable_irq, + .ack = spider_ack_irq, + .end = spider_end_irq, +}; + + +int spider_get_irq(unsigned long int_pending) +{ + void __iomem *regs = spider_get_pic(int_pending); + unsigned long cs; + int irq; + + cs = in_be32(regs + TIR_CS); + + irq = cs >> 24; + if (irq != 63) + return irq; + + return -1; +} + +void spider_init_IRQ(void) +{ + int node; + struct device_node *dn; + unsigned int *property; + long spiderpic; + int n; + +/* FIXME: detect multiple PICs as soon as the device tree has them */ + for (node = 0; node < 1; node++) { + dn = of_find_node_by_path("/"); + n = prom_n_addr_cells(dn); + property = (unsigned int *) get_property(dn, + "platform-spider-pic", NULL); + + if (!property) + continue; + for (spiderpic = 0; n > 0; --n) + spiderpic = (spiderpic << 32) + *property++; + printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic); + spider_pics[node] = __ioremap(spiderpic, 0x800, _PAGE_NO_CACHE); + for (n = 0; n < IIC_NUM_EXT; n++) { + int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE; + get_irq_desc(irq)->handler = &spider_pic; + + /* do not mask any interrupts because of level */ + out_be32(spider_pics[node] + TIR_MSK, 0x0); + + /* disable edge detection clear */ + /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */ + + /* enable interrupt packets to be output */ + out_be32(spider_pics[node] + TIR_PIEN, + in_be32(spider_pics[node] + TIR_PIEN) | 0x1); + + /* Enable the interrupt detection enable bit. Do this last! */ + out_be32(spider_pics[node] + TIR_DEN, + in_be32(spider_pics[node] +TIR_DEN) | 0x1); + + } + } +} diff --git a/arch/ppc64/kernel/time.c b/arch/ppc64/kernel/time.c index 33364a7d2cd2..2348a75e050d 100644 --- a/arch/ppc64/kernel/time.c +++ b/arch/ppc64/kernel/time.c @@ -107,6 +107,9 @@ void ppc_adjtimex(void); static unsigned adjusting_time = 0; +unsigned long ppc_proc_freq; +unsigned long ppc_tb_freq; + static __inline__ void timer_check_rtc(void) { /* @@ -472,6 +475,66 @@ int do_settimeofday(struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_MAPLE) || defined(CONFIG_PPC_BPA) +void __init generic_calibrate_decr(void) +{ + struct device_node *cpu; + struct div_result divres; + unsigned int *fp; + int node_found; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = of_find_node_by_type(NULL, "cpu"); + + ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */ + node_found = 0; + if (cpu != 0) { + fp = (unsigned int *)get_property(cpu, "timebase-frequency", + NULL); + if (fp != 0) { + node_found = 1; + ppc_tb_freq = *fp; + } + } + if (!node_found) + printk(KERN_ERR "WARNING: Estimating decrementer frequency " + "(not found)\n"); + + ppc_proc_freq = DEFAULT_PROC_FREQ; + node_found = 0; + if (cpu != 0) { + fp = (unsigned int *)get_property(cpu, "clock-frequency", + NULL); + if (fp != 0) { + node_found = 1; + ppc_proc_freq = *fp; + } + } + if (!node_found) + printk(KERN_ERR "WARNING: Estimating processor frequency " + "(not found)\n"); + + of_node_put(cpu); + + printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", + ppc_tb_freq/1000000, ppc_tb_freq%1000000); + printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", + ppc_proc_freq/1000000, ppc_proc_freq%1000000); + + tb_ticks_per_jiffy = ppc_tb_freq / HZ; + tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; + tb_ticks_per_usec = ppc_tb_freq / 1000000; + tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); + div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); + tb_to_xs = divres.result_low; + + setup_default_decr(); +} +#endif + void __init time_init(void) { /* This function is only called on the boot processor */ diff --git a/arch/ppc64/kernel/traps.c b/arch/ppc64/kernel/traps.c index 7e52cb2605e0..a8d5e83ee89f 100644 --- a/arch/ppc64/kernel/traps.c +++ b/arch/ppc64/kernel/traps.c @@ -126,6 +126,10 @@ int die(const char *str, struct pt_regs *regs, long err) printk("POWERMAC "); nl = 1; break; + case PLATFORM_BPA: + printk("BPA "); + nl = 1; + break; } if (nl) printk("\n"); diff --git a/arch/ppc64/mm/Makefile b/arch/ppc64/mm/Makefile index ac522d57b2a7..3695d00d347f 100644 --- a/arch/ppc64/mm/Makefile +++ b/arch/ppc64/mm/Makefile @@ -6,6 +6,6 @@ EXTRA_CFLAGS += -mno-minimal-toc obj-y := fault.o init.o imalloc.o hash_utils.o hash_low.o tlb.o \ slb_low.o slb.o stab.o mmap.o -obj-$(CONFIG_DISCONTIGMEM) += numa.o +obj-$(CONFIG_NEED_MULTIPLE_NODES) += numa.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o obj-$(CONFIG_PPC_MULTIPLATFORM) += hash_native.o diff --git a/arch/ppc64/mm/init.c b/arch/ppc64/mm/init.c index 6fa1e6490b57..b50b3a446dbe 100644 --- a/arch/ppc64/mm/init.c +++ b/arch/ppc64/mm/init.c @@ -98,7 +98,7 @@ void show_mem(void) printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); for_each_pgdat(pgdat) { for (i = 0; i < pgdat->node_spanned_pages; i++) { - page = pgdat->node_mem_map + i; + page = pgdat_page_nr(pgdat, i); total++; if (PageReserved(page)) reserved++; @@ -531,7 +531,7 @@ EXPORT_SYMBOL(page_is_ram); * Initialize the bootmem system and give it all the memory we * have available. */ -#ifndef CONFIG_DISCONTIGMEM +#ifndef CONFIG_NEED_MULTIPLE_NODES void __init do_init_bootmem(void) { unsigned long i; @@ -553,12 +553,20 @@ void __init do_init_bootmem(void) max_pfn = max_low_pfn; - /* add all physical memory to the bootmem map. Also find the first */ + /* Add all physical memory to the bootmem map, mark each area + * present. + */ for (i=0; i < lmb.memory.cnt; i++) { unsigned long physbase, size; + unsigned long start_pfn, end_pfn; physbase = lmb.memory.region[i].physbase; size = lmb.memory.region[i].size; + + start_pfn = physbase >> PAGE_SHIFT; + end_pfn = start_pfn + (size >> PAGE_SHIFT); + memory_present(0, start_pfn, end_pfn); + free_bootmem(physbase, size); } @@ -597,7 +605,7 @@ void __init paging_init(void) free_area_init_node(0, NODE_DATA(0), zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, zholes_size); } -#endif /* CONFIG_DISCONTIGMEM */ +#endif /* ! CONFIG_NEED_MULTIPLE_NODES */ static struct kcore_list kcore_vmem; @@ -628,7 +636,7 @@ module_init(setup_kcore); void __init mem_init(void) { -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NEED_MULTIPLE_NODES int nid; #endif pg_data_t *pgdat; @@ -639,7 +647,7 @@ void __init mem_init(void) num_physpages = max_low_pfn; /* RAM is assumed contiguous */ high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NEED_MULTIPLE_NODES for_each_online_node(nid) { if (NODE_DATA(nid)->node_spanned_pages != 0) { printk("freeing bootmem node %x\n", nid); @@ -654,7 +662,7 @@ void __init mem_init(void) for_each_pgdat(pgdat) { for (i = 0; i < pgdat->node_spanned_pages; i++) { - page = pgdat->node_mem_map + i; + page = pgdat_page_nr(pgdat, i); if (PageReserved(page)) reservedpages++; } diff --git a/arch/ppc64/mm/numa.c b/arch/ppc64/mm/numa.c index ea862ec643d3..cafd91aef289 100644 --- a/arch/ppc64/mm/numa.c +++ b/arch/ppc64/mm/numa.c @@ -440,6 +440,8 @@ new_range: for (i = start ; i < (start+size); i += MEMORY_INCREMENT) numa_memory_lookup_table[i >> MEMORY_INCREMENT_SHIFT] = numa_domain; + memory_present(numa_domain, start >> PAGE_SHIFT, + (start + size) >> PAGE_SHIFT); if (--ranges) goto new_range; @@ -481,6 +483,7 @@ static void __init setup_nonnuma(void) for (i = 0 ; i < top_of_ram; i += MEMORY_INCREMENT) numa_memory_lookup_table[i >> MEMORY_INCREMENT_SHIFT] = 0; + memory_present(0, 0, init_node_data[0].node_end_pfn); } static void __init dump_numa_topology(void) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ab79af84699a..32696c1d9280 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -226,6 +226,8 @@ config WARN_STACK_SIZE This allows you to specify the maximum frame size a function may have without the compiler complaining about it. +source "mm/Kconfig" + comment "I/O subsystem configuration" config MACHCHK_WARNING diff --git a/arch/s390/boot/install.sh b/arch/s390/boot/install.sh index 278a8139cb18..d4026f62cb06 100644 --- a/arch/s390/boot/install.sh +++ b/arch/s390/boot/install.sh @@ -21,8 +21,8 @@ # 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 +if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi +if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then exec /sbin/${CROSS_COMPILE}installkernel "$@"; fi # Default install - same as make zlilo diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index bf33dcfec7db..3898f66d0b2f 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h @@ -45,7 +45,7 @@ typedef struct compat_siginfo { /* POSIX.1b timers */ struct { - timer_t _tid; /* timer id */ + compat_timer_t _tid; /* timer id */ int _overrun; /* overrun count */ compat_sigval_t _sigval; /* same as below */ int _sys_private; /* not to be passed to user */ diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 3468d5127223..a7c8bfc11604 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -486,7 +486,7 @@ config CPU_SUBTYPE_ST40 depends on CPU_SUBTYPE_ST40STB1 || CPU_SUBTYPE_ST40GX1 default y -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool depends on SH_HP690 default y @@ -496,6 +496,8 @@ config DISCONTIGMEM or have huge holes in the physical address space for other reasons. See for more. +source "mm/Kconfig" + config ZERO_PAGE_OFFSET hex "Zero page offset" default "0x00001000" if !(SH_MPC1211 || SH_SH03) diff --git a/arch/sh64/Kconfig b/arch/sh64/Kconfig index 76eb81fba45e..708e59736a4d 100644 --- a/arch/sh64/Kconfig +++ b/arch/sh64/Kconfig @@ -217,6 +217,8 @@ config PREEMPT bool "Preemptible Kernel (EXPERIMENTAL)" depends on EXPERIMENTAL +source "mm/Kconfig" + endmenu menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)" diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 237f922520fd..262e13d086fe 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -264,7 +264,11 @@ config SUNOS_EMUL want to run SunOS binaries on an Ultra you must also say Y to "Kernel support for 32-bit a.out binaries" above. -source "drivers/parport/Kconfig" +source "mm/Kconfig" + +endmenu + +source "drivers/Kconfig" config PRINTER tristate "Parallel printer support" @@ -291,6 +295,8 @@ config PRINTER 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. +source "mm/Kconfig" + endmenu source "drivers/base/Kconfig" @@ -372,18 +378,8 @@ config UNIX98_PTY_COUNT endmenu -source "drivers/input/Kconfig" - source "fs/Kconfig" -source "sound/Kconfig" - -source "drivers/usb/Kconfig" - -source "drivers/infiniband/Kconfig" - -source "drivers/char/watchdog/Kconfig" - source "arch/sparc/Kconfig.debug" source "security/Kconfig" diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index a72fd15d5ea8..e2b050eb3b96 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -484,6 +484,8 @@ config CMDLINE NOTE: This option WILL override the PROM bootargs setting! +source "mm/Kconfig" + endmenu source "drivers/base/Kconfig" diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index 7066d7ba667a..bdac631cf011 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c @@ -6,7 +6,6 @@ #include #include #include - #include #include @@ -47,25 +46,59 @@ void arch_copy_kprobe(struct kprobe *p) { p->ainsn.insn[0] = *p->addr; p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2; + p->opcode = *p->addr; +} + +void arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flushi(p->addr); +} + +void arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flushi(p->addr); } void arch_remove_kprobe(struct kprobe *p) { } -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 - static struct kprobe *current_kprobe; static unsigned long current_kprobe_orig_tnpc; static unsigned long current_kprobe_orig_tstate_pil; static unsigned int kprobe_status; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_orig_tnpc_prev; +static unsigned long kprobe_orig_tstate_pil_prev; +static unsigned int kprobe_status_prev; -static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +static inline void save_previous_kprobe(void) +{ + kprobe_status_prev = kprobe_status; + kprobe_orig_tnpc_prev = current_kprobe_orig_tnpc; + kprobe_orig_tstate_pil_prev = current_kprobe_orig_tstate_pil; + kprobe_prev = current_kprobe; +} + +static inline void restore_previous_kprobe(void) +{ + kprobe_status = kprobe_status_prev; + current_kprobe_orig_tnpc = kprobe_orig_tnpc_prev; + current_kprobe_orig_tstate_pil = kprobe_orig_tstate_pil_prev; + current_kprobe = kprobe_prev; +} + +static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs) { current_kprobe_orig_tnpc = regs->tnpc; current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL); + current_kprobe = p; +} + +static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ regs->tstate |= TSTATE_PIL; /*single step inline, if it a breakpoint instruction*/ @@ -78,17 +111,6 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) } } -static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) -{ - *p->addr = p->opcode; - flushi(p->addr); - - regs->tpc = (unsigned long) p->addr; - regs->tnpc = current_kprobe_orig_tnpc; - regs->tstate = ((regs->tstate & ~TSTATE_PIL) | - current_kprobe_orig_tstate_pil); -} - static int kprobe_handler(struct pt_regs *regs) { struct kprobe *p; @@ -109,8 +131,18 @@ static int kprobe_handler(struct pt_regs *regs) unlock_kprobes(); goto no_kprobe; } - disarm_kprobe(p, regs); - ret = 1; + /* We have reentered the kprobe_handler(), since + * another probe was hit while within the handler. + * We here save the original kprobes variables and + * just single step on the instruction of the new probe + * without calling any user handlers. + */ + save_previous_kprobe(); + set_current_kprobe(p, regs); + p->nmissed++; + kprobe_status = KPROBE_REENTER; + prepare_singlestep(p, regs); + return 1; } else { p = current_kprobe; if (p->break_handler && p->break_handler(p, regs)) @@ -138,8 +170,8 @@ static int kprobe_handler(struct pt_regs *regs) goto no_kprobe; } + set_current_kprobe(p, regs); kprobe_status = KPROBE_HIT_ACTIVE; - current_kprobe = p; if (p->pre_handler && p->pre_handler(p, regs)) return 1; @@ -245,12 +277,20 @@ static inline int post_kprobe_handler(struct pt_regs *regs) if (!kprobe_running()) return 0; - if (current_kprobe->post_handler) + if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { + kprobe_status = KPROBE_HIT_SSDONE; current_kprobe->post_handler(current_kprobe, regs, 0); + } resume_execution(current_kprobe, regs); + /*Restore back the original saved kprobes variables and continue. */ + if (kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(); + goto out; + } unlock_kprobes(); +out: preempt_enable_no_resched(); return 1; @@ -392,3 +432,4 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) } return 0; } + diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 9a375e975cff..f28428f4170e 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -102,7 +102,7 @@ typedef struct compat_siginfo{ /* POSIX.1b timers */ struct { - timer_t _tid; /* timer id */ + compat_timer_t _tid; /* timer id */ int _overrun; /* overrun count */ compat_sigval_t _sigval; /* same as below */ int _sys_private; /* not to be passed to user */ diff --git a/arch/um/Kconfig b/arch/um/Kconfig index b8e952c88fd1..9469e77303e6 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -74,6 +74,7 @@ config MODE_SKAS option will shrink the UML binary slightly. source "arch/um/Kconfig_arch" +source "mm/Kconfig" config LD_SCRIPT_STATIC bool diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c index 804c6bbdf67c..157584ae4792 100644 --- a/arch/um/kernel/process_kern.c +++ b/arch/um/kernel/process_kern.c @@ -8,6 +8,7 @@ #include "linux/kernel.h" #include "linux/sched.h" #include "linux/interrupt.h" +#include "linux/string.h" #include "linux/mm.h" #include "linux/slab.h" #include "linux/utsname.h" @@ -322,12 +323,7 @@ void do_uml_exitcalls(void) char *uml_strdup(char *string) { - char *new; - - new = kmalloc(strlen(string) + 1, GFP_KERNEL); - if(new == NULL) return(NULL); - strcpy(new, string); - return(new); + return kstrdup(string, GFP_KERNEL); } int copy_to_user_proc(void __user *to, void *from, int size) diff --git a/arch/v850/Kconfig b/arch/v850/Kconfig index 90cd4baa75ee..27febd6ffa80 100644 --- a/arch/v850/Kconfig +++ b/arch/v850/Kconfig @@ -218,6 +218,8 @@ menu "Processor type and features" a lot of RAM, and you need to able to allocate very large contiguous chunks. If unsure, say N. +source "mm/Kconfig" + endmenu diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 289f448ac89c..db259757dc8a 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -265,7 +265,7 @@ config NUMA_EMU into virtual nodes when booted with "numa=fake=N", where N is the number of nodes. This is only useful for debugging. -config DISCONTIGMEM +config ARCH_DISCONTIGMEM_ENABLE bool depends on NUMA default y @@ -274,6 +274,27 @@ config NUMA bool default n +config ARCH_DISCONTIGMEM_ENABLE + def_bool y + depends on NUMA + +config ARCH_DISCONTIGMEM_DEFAULT + def_bool y + depends on NUMA + +config ARCH_SPARSEMEM_ENABLE + def_bool y + depends on NUMA + +config ARCH_FLATMEM_ENABLE + def_bool y + depends on !NUMA + +source "mm/Kconfig" + +config HAVE_ARCH_EARLY_PFN_TO_NID + def_bool y + config HAVE_DEC_LOCK bool depends on SMP @@ -381,6 +402,8 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +source kernel/Kconfig.hz + endmenu # diff --git a/arch/x86_64/boot/install.sh b/arch/x86_64/boot/install.sh index 90f2452b3b9e..f17b40dfc0f4 100644 --- a/arch/x86_64/boot/install.sh +++ b/arch/x86_64/boot/install.sh @@ -21,8 +21,8 @@ # 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 +if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi +if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then exec /sbin/${CROSS_COMPILE}installkernel "$@"; fi # Default install - same as make zlilo diff --git a/arch/x86_64/ia32/ia32_signal.c b/arch/x86_64/ia32/ia32_signal.c index fbd09b5126ce..66e2821533db 100644 --- a/arch/x86_64/ia32/ia32_signal.c +++ b/arch/x86_64/ia32/ia32_signal.c @@ -428,8 +428,8 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) return (void __user *)((rsp - frame_size) & -8UL); } -void ia32_setup_frame(int sig, struct k_sigaction *ka, - compat_sigset_t *set, struct pt_regs * regs) +int ia32_setup_frame(int sig, struct k_sigaction *ka, + compat_sigset_t *set, struct pt_regs * regs) { struct sigframe __user *frame; int err = 0; @@ -514,14 +514,15 @@ void ia32_setup_frame(int sig, struct k_sigaction *ka, current->comm, current->pid, frame, regs->rip, frame->pretcode); #endif - return; + return 1; give_sigsegv: force_sigsegv(sig, current); + return 0; } -void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - compat_sigset_t *set, struct pt_regs * regs) +int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + compat_sigset_t *set, struct pt_regs * regs) { struct rt_sigframe __user *frame; int err = 0; @@ -613,9 +614,9 @@ void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, current->comm, current->pid, frame, regs->rip, frame->pretcode); #endif - return; + return 1; give_sigsegv: force_sigsegv(sig, current); + return 0; } - diff --git a/arch/x86_64/kernel/aperture.c b/arch/x86_64/kernel/aperture.c index 504e63474993..c9a6b812e926 100644 --- a/arch/x86_64/kernel/aperture.c +++ b/arch/x86_64/kernel/aperture.c @@ -40,11 +40,7 @@ int fix_aperture __initdata = 1; static u32 __init allocate_aperture(void) { -#ifdef CONFIG_DISCONTIGMEM pg_data_t *nd0 = NODE_DATA(0); -#else - pg_data_t *nd0 = &contig_page_data; -#endif u32 aper_size; void *p; diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index e3a19e8ebbf8..9631c747c5e3 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -2,20 +2,24 @@ #include #include #include +#include #include #include /* Simple VGA output */ #ifdef __i386__ +#include #define VGABASE (__ISA_IO_base + 0xb8000) #else +#include #define VGABASE ((void __iomem *)0xffffffff800b8000UL) #endif -#define MAX_YPOS 25 -#define MAX_XPOS 80 +#define MAX_YPOS max_ypos +#define MAX_XPOS max_xpos +static int max_ypos = 25, max_xpos = 80; static int current_ypos = 1, current_xpos = 0; static void early_vga_write(struct console *con, const char *str, unsigned n) @@ -196,7 +200,10 @@ int __init setup_early_printk(char *opt) } else if (!strncmp(buf, "ttyS", 4)) { early_serial_init(buf); early_console = &early_serial_console; - } else if (!strncmp(buf, "vga", 3)) { + } else if (!strncmp(buf, "vga", 3) + && SCREEN_INFO.orig_video_isVGA == 1) { + max_xpos = SCREEN_INFO.orig_video_cols; + max_ypos = SCREEN_INFO.orig_video_lines; early_console = &early_vga_console; } early_console_initialized = 1; diff --git a/arch/x86_64/kernel/head64.c b/arch/x86_64/kernel/head64.c index 0f8c78dcd38c..cf6ab147a2a5 100644 --- a/arch/x86_64/kernel/head64.c +++ b/arch/x86_64/kernel/head64.c @@ -94,7 +94,7 @@ void __init x86_64_start_kernel(char * real_mode_data) s = strstr(saved_command_line, "earlyprintk="); if (s != NULL) setup_early_printk(s); -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA s = strstr(saved_command_line, "numa="); if (s != NULL) numa_setup(s+5); diff --git a/arch/x86_64/kernel/i8259.c b/arch/x86_64/kernel/i8259.c index 7873d9ba8814..19eafa0aa95c 100644 --- a/arch/x86_64/kernel/i8259.c +++ b/arch/x86_64/kernel/i8259.c @@ -157,14 +157,13 @@ static unsigned int startup_8259A_irq(unsigned int irq) } 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 + .typename = "XT-PIC", + .startup = startup_8259A_irq, + .shutdown = shutdown_8259A_irq, + .enable = enable_8259A_irq, + .disable = disable_8259A_irq, + .ack = mask_and_ack_8259A, + .end = end_8259A_irq, }; /* diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c index f77f8a0ff187..4e680f87a75f 100644 --- a/arch/x86_64/kernel/kprobes.c +++ b/arch/x86_64/kernel/kprobes.c @@ -27,6 +27,8 @@ * adapted for x86_64 * 2005-Mar Roland McGrath * Fixed to handle %rip-relative addressing mode correctly. + * 2005-May Rusty Lynch + * Added function return probes functionality */ #include @@ -37,18 +39,16 @@ #include #include #include - +#include #include #include static DECLARE_MUTEX(kprobe_mutex); -/* kprobe_status settings */ -#define KPROBE_HIT_ACTIVE 0x00000001 -#define KPROBE_HIT_SS 0x00000002 - static struct kprobe *current_kprobe; static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags; +static struct kprobe *kprobe_prev; +static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev; static struct pt_regs jprobe_saved_regs; static long *jprobe_saved_rsp; static kprobe_opcode_t *get_insn_slot(void); @@ -214,6 +214,21 @@ void arch_copy_kprobe(struct kprobe *p) BUG_ON((s64) (s32) disp != disp); /* Sanity check. */ *ripdisp = disp; } + p->opcode = *p->addr; +} + +void arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); +} + +void arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); } void arch_remove_kprobe(struct kprobe *p) @@ -223,10 +238,29 @@ void arch_remove_kprobe(struct kprobe *p) down(&kprobe_mutex); } -static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +static inline void save_previous_kprobe(void) { - *p->addr = p->opcode; - regs->rip = (unsigned long)p->addr; + kprobe_prev = current_kprobe; + kprobe_status_prev = kprobe_status; + kprobe_old_rflags_prev = kprobe_old_rflags; + kprobe_saved_rflags_prev = kprobe_saved_rflags; +} + +static inline void restore_previous_kprobe(void) +{ + current_kprobe = kprobe_prev; + kprobe_status = kprobe_status_prev; + kprobe_old_rflags = kprobe_old_rflags_prev; + kprobe_saved_rflags = kprobe_saved_rflags_prev; +} + +static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + current_kprobe = p; + kprobe_saved_rflags = kprobe_old_rflags + = (regs->eflags & (TF_MASK | IF_MASK)); + if (is_IF_modifier(p->ainsn.insn)) + kprobe_saved_rflags &= ~IF_MASK; } static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) @@ -240,6 +274,50 @@ static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) regs->rip = (unsigned long)p->ainsn.insn; } +struct task_struct *arch_get_kprobe_task(void *ptr) +{ + return ((struct thread_info *) (((unsigned long) ptr) & + (~(THREAD_SIZE -1))))->task; +} + +void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) +{ + unsigned long *sara = (unsigned long *)regs->rsp; + struct kretprobe_instance *ri; + static void *orig_ret_addr; + + /* + * Save the return address when the return probe hits + * the first time, and use it to populate the (krprobe + * instance)->ret_addr for subsequent return probes at + * the same addrress since stack address would have + * the kretprobe_trampoline by then. + */ + if (((void*) *sara) != kretprobe_trampoline) + orig_ret_addr = (void*) *sara; + + if ((ri = get_free_rp_inst(rp)) != NULL) { + ri->rp = rp; + ri->stack_addr = sara; + ri->ret_addr = orig_ret_addr; + add_rp_inst(ri); + /* Replace the return addr with trampoline addr */ + *sara = (unsigned long) &kretprobe_trampoline; + } else { + rp->nmissed++; + } +} + +void arch_kprobe_flush_task(struct task_struct *tk) +{ + struct kretprobe_instance *ri; + while ((ri = get_rp_inst_tsk(tk)) != NULL) { + *((unsigned long *)(ri->stack_addr)) = + (unsigned long) ri->ret_addr; + recycle_rp_inst(ri); + } +} + /* * Interrupts are disabled on entry as trap3 is an interrupt gate and they * remain disabled thorough out this function. @@ -264,9 +342,30 @@ int kprobe_handler(struct pt_regs *regs) regs->eflags |= kprobe_saved_rflags; unlock_kprobes(); goto no_kprobe; + } else if (kprobe_status == KPROBE_HIT_SSDONE) { + /* TODO: Provide re-entrancy from + * post_kprobes_handler() and avoid exception + * stack corruption while single-stepping on + * the instruction of the new probe. + */ + arch_disarm_kprobe(p); + regs->rip = (unsigned long)p->addr; + ret = 1; + } else { + /* We have reentered the kprobe_handler(), since + * another probe was hit while within the + * handler. We here save the original kprobe + * variables and just single step on instruction + * of the new probe without calling any user + * handlers. + */ + save_previous_kprobe(); + set_current_kprobe(p, regs); + p->nmissed++; + prepare_singlestep(p, regs); + kprobe_status = KPROBE_REENTER; + return 1; } - disarm_kprobe(p, regs); - ret = 1; } else { p = current_kprobe; if (p->break_handler && p->break_handler(p, regs)) { @@ -296,11 +395,7 @@ int kprobe_handler(struct pt_regs *regs) } kprobe_status = KPROBE_HIT_ACTIVE; - current_kprobe = p; - kprobe_saved_rflags = kprobe_old_rflags - = (regs->eflags & (TF_MASK | IF_MASK)); - if (is_IF_modifier(p->ainsn.insn)) - kprobe_saved_rflags &= ~IF_MASK; + set_current_kprobe(p, regs); if (p->pre_handler && p->pre_handler(p, regs)) /* handler has already set things up, so skip ss setup */ @@ -316,6 +411,55 @@ no_kprobe: return ret; } +/* + * For function-return probes, init_kprobes() establishes a probepoint + * here. When a retprobed function returns, this probe is hit and + * trampoline_probe_handler() runs, calling the kretprobe's handler. + */ + void kretprobe_trampoline_holder(void) + { + asm volatile ( ".global kretprobe_trampoline\n" + "kretprobe_trampoline: \n" + "nop\n"); + } + +/* + * Called when we hit the probe point at kretprobe_trampoline + */ +int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct task_struct *tsk; + struct kretprobe_instance *ri; + struct hlist_head *head; + struct hlist_node *node; + unsigned long *sara = (unsigned long *)regs->rsp - 1; + + tsk = arch_get_kprobe_task(sara); + head = kretprobe_inst_table_head(tsk); + + hlist_for_each_entry(ri, node, head, hlist) { + if (ri->stack_addr == sara && ri->rp) { + if (ri->rp->handler) + ri->rp->handler(ri, regs); + } + } + return 0; +} + +void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + struct kretprobe_instance *ri; + /* RA already popped */ + unsigned long *sara = ((unsigned long *)regs->rsp) - 1; + + while ((ri = get_rp_inst(sara))) { + regs->rip = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri); + } + regs->eflags &= ~TF_MASK; +} + /* * Called after single-stepping. p->addr is the address of the * instruction whose first byte has been replaced by the "int 3" @@ -401,13 +545,23 @@ int post_kprobe_handler(struct pt_regs *regs) if (!kprobe_running()) return 0; - if (current_kprobe->post_handler) + if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { + kprobe_status = KPROBE_HIT_SSDONE; current_kprobe->post_handler(current_kprobe, regs, 0); + } - resume_execution(current_kprobe, regs); + if (current_kprobe->post_handler != trampoline_post_handler) + resume_execution(current_kprobe, regs); regs->eflags |= kprobe_saved_rflags; - unlock_kprobes(); + /* Restore the original saved kprobes variables and continue. */ + if (kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(); + goto out; + } else { + unlock_kprobes(); + } +out: preempt_enable_no_resched(); /* diff --git a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c index 61a63be6b294..9c5aa2a790c7 100644 --- a/arch/x86_64/kernel/mpparse.c +++ b/arch/x86_64/kernel/mpparse.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,8 @@ int acpi_found_madt; int apic_version [MAX_APICS]; unsigned char 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 }; -cpumask_t pci_bus_to_cpumask [256] = { [0 ... 255] = CPU_MASK_ALL }; +unsigned char pci_bus_to_node [256]; +EXPORT_SYMBOL(pci_bus_to_node); static int mp_current_pci_id = 0; /* I/O APIC entries */ @@ -904,11 +906,20 @@ void __init mp_config_acpi_legacy_irqs (void) return; } +#define MAX_GSI_NUM 4096 + int mp_register_gsi(u32 gsi, int edge_level, int active_high_low) { int ioapic = -1; int ioapic_pin = 0; int idx, bit = 0; + static int pci_irq = 16; + /* + * Mapping between Global System Interrupts, which + * represent all possible interrupts, to the IRQs + * assigned to actual devices. + */ + static int gsi_to_irq[MAX_GSI_NUM]; if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC) return gsi; @@ -943,11 +954,21 @@ int mp_register_gsi(u32 gsi, int edge_level, int active_high_low) if ((1< #include #include +#include #include #include @@ -293,6 +294,14 @@ void exit_thread(void) { struct task_struct *me = current; struct thread_struct *t = &me->thread; + + /* + * Remove function-return probe instances associated with this task + * and put them back on the free list. Do not insert an exit probe for + * this function, it will be disabled by kprobe_flush_task if you do. + */ + kprobe_flush_task(me); + if (me->thread.io_bitmap_ptr) { struct tss_struct *tss = &per_cpu(init_tss, get_cpu()); @@ -312,6 +321,13 @@ void flush_thread(void) struct task_struct *tsk = current; struct thread_info *t = current_thread_info(); + /* + * Remove function-return probe instances associated with this task + * and put them back on the free list. Do not insert an exit probe for + * this function, it will be disabled by kprobe_flush_task if you do. + */ + kprobe_flush_task(tsk); + if (t->flags & _TIF_ABI_PENDING) t->flags ^= (_TIF_ABI_PENDING | _TIF_IA32); diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index 99f038ede23c..000015dd5a8b 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c @@ -40,6 +40,8 @@ #include #include #include +#include + #include #include #include @@ -348,7 +350,7 @@ static __init void parse_cmdline_early (char ** cmdline_p) if (!memcmp(from, "mem=", 4)) parse_memopt(from+4, &from); -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA if (!memcmp(from, "numa=", 5)) numa_setup(from+5); #endif @@ -377,17 +379,20 @@ static __init void parse_cmdline_early (char ** cmdline_p) *cmdline_p = command_line; } -#ifndef CONFIG_DISCONTIGMEM -static void __init contig_initmem_init(void) +#ifndef CONFIG_NUMA +static void __init +contig_initmem_init(unsigned long start_pfn, unsigned long end_pfn) { - unsigned long bootmap_size, bootmap; - bootmap_size = bootmem_bootmap_pages(end_pfn)<> PAGE_SHIFT, end_pfn); - e820_bootmem_free(&contig_page_data, 0, end_pfn << PAGE_SHIFT); - reserve_bootmem(bootmap, bootmap_size); + unsigned long bootmap_size, bootmap; + + memory_present(0, start_pfn, end_pfn); + bootmap_size = bootmem_bootmap_pages(end_pfn)<> PAGE_SHIFT, end_pfn); + e820_bootmem_free(NODE_DATA(0), 0, end_pfn << PAGE_SHIFT); + reserve_bootmem(bootmap, bootmap_size); } #endif @@ -554,10 +559,10 @@ void __init setup_arch(char **cmdline_p) acpi_numa_init(); #endif -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA numa_initmem_init(0, end_pfn); #else - contig_initmem_init(); + contig_initmem_init(0, end_pfn); #endif /* Reserve direct mapping */ @@ -618,6 +623,8 @@ void __init setup_arch(char **cmdline_p) } } #endif + + sparse_init(); paging_init(); check_ioapic(); diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c index 429c0269dc4e..98b7ba95d581 100644 --- a/arch/x86_64/kernel/signal.c +++ b/arch/x86_64/kernel/signal.c @@ -34,9 +34,9 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +int 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, +int ia32_setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs); asmlinkage long @@ -238,7 +238,7 @@ get_stack(struct k_sigaction *ka, struct pt_regs *regs, unsigned long size) return (void __user *)round_down(rsp - size, 16); } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { struct rt_sigframe __user *frame; @@ -327,20 +327,23 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, current->comm, current->pid, frame, regs->rip, frame->pretcode); #endif - return; + return 1; give_sigsegv: force_sigsegv(sig, current); + return 0; } /* * OK, we're invoking a handler */ -static void +static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) { + int ret; + #ifdef DEBUG_SIG printk("handle_signal pid:%d sig:%lu rip:%lx rsp:%lx regs=%p\n", current->pid, sig, @@ -384,20 +387,22 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, #ifdef CONFIG_IA32_EMULATION if (test_thread_flag(TIF_IA32)) { if (ka->sa.sa_flags & SA_SIGINFO) - ia32_setup_rt_frame(sig, ka, info, oldset, regs); + ret = ia32_setup_rt_frame(sig, ka, info, oldset, regs); else - ia32_setup_frame(sig, ka, oldset, regs); + ret = ia32_setup_frame(sig, ka, oldset, regs); } else #endif - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); - if (!(ka->sa.sa_flags & SA_NODEFER)) { + if (ret && !(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); } + + return ret; } /* @@ -417,7 +422,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) * kernel mode. Just return without doing anything * if so. */ - if ((regs->cs & 3) != 3) + if (!user_mode(regs)) return 1; if (try_to_freeze(0)) @@ -434,11 +439,10 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) * inside the kernel. */ if (current->thread.debugreg7) - asm volatile("movq %0,%%db7" : : "r" (current->thread.debugreg7)); + set_debugreg(current->thread.debugreg7, 7); /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, &ka, oldset, regs); - return 1; + return handle_signal(signr, &info, &ka, oldset, regs); } no_signal: diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index fb8c809b4cd9..66bf6ddeb0c3 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -64,6 +64,7 @@ static int notsc __initdata = 0; unsigned int cpu_khz; /* TSC clocks / usec, not used here */ static unsigned long hpet_period; /* fsecs / HPET clock */ unsigned long hpet_tick; /* HPET clocks / interrupt */ +static int hpet_use_timer; unsigned long vxtime_hz = PIT_TICK_RATE; int report_lost_ticks; /* command line option */ unsigned long long monotonic_base; @@ -105,7 +106,9 @@ static inline unsigned int do_gettimeoffset_tsc(void) static inline unsigned int do_gettimeoffset_hpet(void) { - return ((hpet_readl(HPET_COUNTER) - vxtime.last) * vxtime.quot) >> 32; + /* cap counter read to one tick to avoid inconsistencies */ + unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last; + return (min(counter,hpet_tick) * vxtime.quot) >> 32; } unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc; @@ -301,7 +304,7 @@ unsigned long long monotonic_clock(void) last_offset = vxtime.last; base = monotonic_base; - this_offset = hpet_readl(HPET_T0_CMP) - hpet_tick; + this_offset = hpet_readl(HPET_COUNTER); } while (read_seqretry(&xtime_lock, seq)); offset = (this_offset - last_offset); @@ -377,7 +380,14 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) write_seqlock(&xtime_lock); - if (vxtime.hpet_address) { + if (vxtime.hpet_address) + offset = hpet_readl(HPET_COUNTER); + + if (hpet_use_timer) { + /* if we're using the hpet timer functionality, + * we can more accurately know the counter value + * when the timer interrupt occured. + */ offset = hpet_readl(HPET_T0_CMP) - hpet_tick; delay = hpet_readl(HPET_COUNTER) - offset; } else { @@ -803,17 +813,18 @@ static int hpet_timer_stop_set_go(unsigned long tick) * Set up timer 0, as periodic with first interrupt to happen at hpet_tick, * and period also hpet_tick. */ - - hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | + if (hpet_use_timer) { + hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | HPET_TN_32BIT, HPET_T0_CFG); - hpet_writel(hpet_tick, HPET_T0_CMP); - hpet_writel(hpet_tick, HPET_T0_CMP); /* AK: why twice? */ - + hpet_writel(hpet_tick, HPET_T0_CMP); + hpet_writel(hpet_tick, HPET_T0_CMP); /* AK: why twice? */ + cfg |= HPET_CFG_LEGACY; + } /* * Go! */ - cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY; + cfg |= HPET_CFG_ENABLE; hpet_writel(cfg, HPET_CFG); return 0; @@ -834,8 +845,7 @@ static int hpet_init(void) id = hpet_readl(HPET_ID); - if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER) || - !(id & HPET_ID_LEGSUP)) + if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER)) return -1; hpet_period = hpet_readl(HPET_PERIOD); @@ -845,6 +855,8 @@ static int hpet_init(void) hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) / hpet_period; + hpet_use_timer = (id & HPET_ID_LEGSUP); + return hpet_timer_stop_set_go(hpet_tick); } @@ -901,9 +913,11 @@ void __init time_init(void) set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); - if (!hpet_init()) { + if (!hpet_init()) vxtime_hz = (1000000000000000L + hpet_period / 2) / hpet_period; + + if (hpet_use_timer) { cpu_khz = hpet_calibrate_tsc(); timename = "HPET"; #ifdef CONFIG_X86_PM_TIMER @@ -968,7 +982,7 @@ void __init time_init_gtod(void) if (unsynchronized_tsc()) notsc = 1; if (vxtime.hpet_address && notsc) { - timetype = "HPET"; + timetype = hpet_use_timer ? "HPET" : "PIT/HPET"; vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick; vxtime.mode = VXTIME_HPET; do_gettimeoffset = do_gettimeoffset_hpet; @@ -983,7 +997,7 @@ void __init time_init_gtod(void) printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n"); #endif } else { - timetype = vxtime.hpet_address ? "HPET/TSC" : "PIT/TSC"; + timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC"; vxtime.mode = VXTIME_TSC; } diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 3dfec8fdabcd..121646fc43f6 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -274,7 +274,7 @@ EXPORT_SYMBOL(dump_stack); void show_registers(struct pt_regs *regs) { int i; - int in_kernel = (regs->cs & 3) == 0; + int in_kernel = !user_mode(regs); unsigned long rsp; const int cpu = safe_smp_processor_id(); struct task_struct *cur = cpu_pda[cpu].pcurrent; @@ -318,7 +318,7 @@ void handle_BUG(struct pt_regs *regs) struct bug_frame f; char tmp; - if (regs->cs & 3) + if (user_mode(regs)) return; if (__copy_from_user(&f, (struct bug_frame *) regs->rip, sizeof(struct bug_frame))) @@ -437,7 +437,7 @@ static void do_trap(int trapnr, int signr, char *str, } #endif - if ((regs->cs & 3) != 0) { + if (user_mode(regs)) { struct task_struct *tsk = current; if (exception_trace && unhandled_signal(tsk, signr)) @@ -522,7 +522,7 @@ asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) } #endif - if ((regs->cs & 3)!=0) { + if (user_mode(regs)) { struct task_struct *tsk = current; if (exception_trace && unhandled_signal(tsk, SIGSEGV)) @@ -638,7 +638,7 @@ asmlinkage struct pt_regs *sync_regs(struct pt_regs *eregs) if (eregs == (struct pt_regs *)eregs->rsp) ; /* Exception from user space */ - else if (eregs->cs & 3) + else if (user_mode(eregs)) regs = ((struct pt_regs *)current->thread.rsp0) - 1; /* Exception from kernel and interrupts are enabled. Move to kernel process stack. */ @@ -669,7 +669,7 @@ asmlinkage void do_debug(struct pt_regs * regs, unsigned long error_code) } #endif - asm("movq %%db6,%0" : "=r" (condition)); + get_debugreg(condition, 6); if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, SIGTRAP) == NOTIFY_STOP) @@ -697,7 +697,7 @@ asmlinkage void do_debug(struct pt_regs * regs, unsigned long error_code) * allowing programs to debug themselves without the ptrace() * interface. */ - if ((regs->cs & 3) == 0) + if (!user_mode(regs)) goto clear_TF_reenable; /* * Was the TF flag set by a debugger? If so, clear it now, @@ -715,13 +715,13 @@ asmlinkage void do_debug(struct pt_regs * regs, unsigned long error_code) info.si_signo = SIGTRAP; info.si_errno = 0; info.si_code = TRAP_BRKPT; - if ((regs->cs & 3) == 0) + if (!user_mode(regs)) goto clear_dr7; info.si_addr = (void __user *)regs->rip; force_sig_info(SIGTRAP, &info, tsk); clear_dr7: - asm volatile("movq %0,%%db7"::"r"(0UL)); + set_debugreg(0UL, 7); return; clear_TF_reenable: @@ -756,7 +756,7 @@ asmlinkage void do_coprocessor_error(struct pt_regs *regs) unsigned short cwd, swd; conditional_sti(regs); - if ((regs->cs & 3) == 0 && + if (!user_mode(regs) && kernel_math_error(regs, "kernel x87 math error")) return; @@ -822,7 +822,7 @@ asmlinkage void do_simd_coprocessor_error(struct pt_regs *regs) unsigned short mxcsr; conditional_sti(regs); - if ((regs->cs & 3) == 0 && + if (!user_mode(regs) && kernel_math_error(regs, "kernel simd math error")) return; diff --git a/arch/x86_64/lib/delay.c b/arch/x86_64/lib/delay.c index aed61a668a1b..33a873a3c223 100644 --- a/arch/x86_64/lib/delay.c +++ b/arch/x86_64/lib/delay.c @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef CONFIG_SMP #include @@ -19,6 +20,12 @@ int x86_udelay_tsc = 0; /* Delay via TSC */ +int read_current_timer(unsigned long *timer_value) +{ + rdtscll(*timer_value); + return 0; +} + void __delay(unsigned long loops) { unsigned bclock, now; diff --git a/arch/x86_64/mm/Makefile b/arch/x86_64/mm/Makefile index 66c354ad80ca..1d232a87f113 100644 --- a/arch/x86_64/mm/Makefile +++ b/arch/x86_64/mm/Makefile @@ -4,7 +4,7 @@ obj-y := init.o fault.o ioremap.o extable.o pageattr.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o -obj-$(CONFIG_DISCONTIGMEM) += numa.o +obj-$(CONFIG_NUMA) += numa.o obj-$(CONFIG_K8_NUMA) += k8topology.o obj-$(CONFIG_ACPI_NUMA) += srat.o diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c index 57d3ab15a5c7..2f187986f940 100644 --- a/arch/x86_64/mm/fault.c +++ b/arch/x86_64/mm/fault.c @@ -74,7 +74,7 @@ static noinline int is_prefetch(struct pt_regs *regs, unsigned long addr, instr = (unsigned char *)convert_rip_to_linear(current, regs); max_instr = instr + 15; - if ((regs->cs & 3) != 0 && instr >= (unsigned char *)TASK_SIZE) + if (user_mode(regs) && instr >= (unsigned char *)TASK_SIZE) return 0; while (scan_more && instr < max_instr) { @@ -106,7 +106,7 @@ static noinline int is_prefetch(struct pt_regs *regs, unsigned long addr, /* Could check the LDT for lm, but for now it's good enough to assume that long mode only uses well known segments or kernel. */ - scan_more = ((regs->cs & 3) == 0) || (regs->cs == __USER_CS); + scan_more = (!user_mode(regs)) || (regs->cs == __USER_CS); break; case 0x60: diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index dbe53b4c7e66..72e4b364ed73 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c @@ -318,7 +318,7 @@ void zap_low_mappings(void) flush_tlb_all(); } -#ifndef CONFIG_DISCONTIGMEM +#ifndef CONFIG_NUMA void __init paging_init(void) { { @@ -427,13 +427,16 @@ void __init mem_init(void) reservedpages = 0; /* this will put all low memory onto the freelists */ -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA totalram_pages += numa_free_all_bootmem(); tmp = 0; /* should count reserved pages here for all nodes */ #else + +#ifdef CONFIG_FLATMEM max_mapnr = end_pfn; if (!mem_map) BUG(); +#endif totalram_pages += free_all_bootmem(); @@ -515,7 +518,7 @@ void free_initrd_mem(unsigned long start, unsigned long end) void __init reserve_bootmem_generic(unsigned long phys, unsigned len) { /* Should check here against the e820 map to avoid double free */ -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA int nid = phys_to_nid(phys); reserve_bootmem_node(NODE_DATA(nid), phys, len); #else diff --git a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c index 58aac23760ef..6972df480d2b 100644 --- a/arch/x86_64/mm/ioremap.c +++ b/arch/x86_64/mm/ioremap.c @@ -178,7 +178,7 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS) return (__force void __iomem *)phys_to_virt(phys_addr); -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_FLATMEM /* * Don't allow anybody to remap normal RAM that we're using.. */ diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index fd9f25d7a6c4..84cde796ecb1 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -66,6 +66,13 @@ int __init compute_hash_shift(struct node *nodes, int numnodes) return -1; } +#ifdef CONFIG_SPARSEMEM +int early_pfn_to_nid(unsigned long pfn) +{ + return phys_to_nid(pfn << PAGE_SHIFT); +} +#endif + /* Initialize bootmem allocator for a node */ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long end) { @@ -80,6 +87,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en start_pfn = start >> PAGE_SHIFT; end_pfn = end >> PAGE_SHIFT; + memory_present(nodeid, start_pfn, end_pfn); nodedata_phys = find_e820_area(start, end, pgdat_size); if (nodedata_phys == -1L) panic("Cannot find memory pgdat in node %d\n", nodeid); diff --git a/arch/x86_64/pci/k8-bus.c b/arch/x86_64/pci/k8-bus.c index 62349c78db57..7e7d0c2a0025 100644 --- a/arch/x86_64/pci/k8-bus.c +++ b/arch/x86_64/pci/k8-bus.c @@ -53,25 +53,11 @@ fill_mp_bus_to_cpumask(void) for (j = SECONDARY_LDT_BUS_NUMBER(ldtbus); j <= SUBORDINATE_LDT_BUS_NUMBER(ldtbus); j++) - pci_bus_to_cpumask[j] = - node_to_cpumask(NODE_ID(nid)); + pci_bus_to_node[j] = NODE_ID(nid); } } } - /* quick sanity check */ - printed = 0; - for (i = 0; i < 256; i++) { - if (cpus_empty(pci_bus_to_cpumask[i])) { - pci_bus_to_cpumask[i] = CPU_MASK_ALL; - if (printed) - continue; - printk(KERN_ERR - "k8-bus.c: some busses have empty cpu mask\n"); - printed = 1; - } - } - return 0; } diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig new file mode 100644 index 000000000000..3e89767cea72 --- /dev/null +++ b/arch/xtensa/Kconfig @@ -0,0 +1,258 @@ +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/config-language.txt. + +mainmenu "Linux/Xtensa Kernel Configuration" + +config FRAME_POINTER + bool + default n + +config XTENSA + bool + default y + help + Xtensa processors are 32-bit RISC machines designed by Tensilica + primarily for embedded systems. These processors are both + configurable and extensible. The Linux port to the Xtensa + architecture supports all processor configurations and extensions, + with reasonable minimum requirements. The Xtensa Linux project has + a home page at . + +config UID16 + bool + default n + +config RWSEM_XCHGADD_ALGORITHM + bool + default y + +config HAVE_DEC_LOCK + bool + default y + +config GENERIC_HARDIRQS + bool + default y + +source "init/Kconfig" + +menu "Processor type and features" + +choice + prompt "Xtensa Processor Configuration" + default XTENSA_CPU_LINUX_BE + +config XTENSA_CPU_LINUX_BE + bool "linux_be" + ---help--- + The linux_be processor configuration is the baseline Xtensa + configurations included in this kernel and also used by + binutils, gcc, and gdb. It contains no TIE, no coprocessors, + and the following configuration options: + + Code Density Option 2 Misc Special Registers + NSA/NSAU Instructions 128-bit Data Bus Width + Processor ID 8K, 2-way I and D Caches + Zero-Overhead Loops 2 Inst Address Break Registers + Big Endian 2 Data Address Break Registers + 64 General-Purpose Registers JTAG Interface and Trace Port + 17 Interrupts MMU w/ TLBs and Autorefill + 3 Interrupt Levels 8 Autorefill Ways (I/D TLBs) + 3 Timers Unaligned Exceptions +endchoice + +config MMU + bool + default y + +config XTENSA_UNALIGNED_USER + bool "Unaligned memory access in use space" + ---help--- + The Xtensa architecture currently does not handle unaligned + memory accesses in hardware but through an exception handler. + Per default, unaligned memory accesses are disabled in user space. + + Say Y here to enable unaligned memory access in user space. + +config PREEMPT + bool "Preemptible Kernel" + ---help--- + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + Unfortunately the kernel code has some race conditions if both + CONFIG_SMP and CONFIG_PREEMPT are enabled, so this option is + currently disabled if you are building an SMP kernel. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + +config MATH_EMULATION + bool "Math emulation" + help + Can we use information of configuration file? + +config HIGHMEM + bool "High memory support" + +endmenu + +menu "Platform options" + +choice + prompt "Xtensa System Type" + default XTENSA_PLATFORM_ISS + +config XTENSA_PLATFORM_ISS + bool "ISS" + help + ISS is an acronym for Tensilica's Instruction Set Simulator. + +config XTENSA_PLATFORM_XT2000 + bool "XT2000" + help + XT2000 is the name of Tensilica's feature-rich emulation platform. + This hardware is capable of running a full Linux distribution. + +endchoice + + +config XTENSA_CALIBRATE_CCOUNT + bool "Auto calibration of the CPU clock rate" + ---help--- + On some platforms (XT2000, for example), the CPU clock rate can + vary. The frequency can be determined, however, by measuring + against a well known, fixed frequency, such as an UART oscillator. + +config XTENSA_CPU_CLOCK + int "CPU clock rate [MHz]" + depends on !XTENSA_CALIBRATE_CCOUNT + default "16" + +config GENERIC_CALIBRATE_DELAY + bool "Auto calibration of the BogoMIPS value" + ---help--- + The BogoMIPS value can easily derived from the CPU frequency. + +config CMDLINE_BOOL + bool "Default bootloader kernel arguments" + +config CMDLINE + string "Initial kernel command string" + depends on CMDLINE_BOOL + default "console=ttyS0,38400 root=/dev/ram" + help + On some architectures (EBSA110 and CATS), there is currently no way + 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). + +config SERIAL_CONSOLE + bool + depends on XTENSA_PLATFORM_ISS + default y + +config XTENSA_ISS_NETWORK + bool + depends on XTENSA_PLATFORM_ISS + default y + +endmenu + +menu "Bus options" + +config PCI + bool "PCI support" if !XTENSA_PLATFORM_ISS + depends on !XTENSA_PLATFORM_ISS + default y + help + 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. + + The PCI-HOWTO, available from + , contains valuable + information about which PCI hardware does work under Linux and which + doesn't + +source "drivers/pci/Kconfig" + +config HOTPLUG + + bool "Support for hot-pluggable devices" + ---help--- + Say Y here if you want to plug devices into your computer while + the system is running, and be able to use them quickly. In many + cases, the devices can likewise be unplugged at any time too. + + One well known example of this is PCMCIA- or PC-cards, credit-card + size devices such as network cards, modems or hard drives which are + plugged into slots found on all modern laptop computers. Another + example, used on modern desktops as well as laptops, is USB. + + Enable HOTPLUG and KMOD, and build a modular kernel. Get agent + 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. + +source "drivers/pcmcia/Kconfig" + +source "drivers/pci/hotplug/Kconfig" + +endmenu + +menu "Exectuable file formats" + +# only elf supported +config KCORE_ELF + bool + depends on PROC_FS + default y + help + If you enabled support for /proc file system then the file + /proc/kcore will contain the kernel core image in ELF format. This + can be used in gdb: + + $ cd /usr/src/linux ; gdb vmlinux /proc/kcore + + 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. + +source "fs/Kconfig.binfmt" + +endmenu + +source "drivers/Kconfig" + +source "fs/Kconfig" + +menu "Xtensa initrd options" + depends on BLK_DEV_INITRD + + config EMBEDDED_RAMDISK + bool "Embed root filesystem ramdisk into the kernel" + +config EMBEDDED_RAMDISK_IMAGE + string "Filename of gziped ramdisk image" + depends on EMBEDDED_RAMDISK + default "ramdisk.gz" + help + This is the filename of the ramdisk image to be built into the + kernel. Relative pathnames are relative to arch/xtensa/boot/ramdisk/. + The ramdisk image is not part of the kernel distribution; you must + provide one yourself. +endmenu + +source "arch/xtensa/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + + diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug new file mode 100644 index 000000000000..11c585295dd7 --- /dev/null +++ b/arch/xtensa/Kconfig.debug @@ -0,0 +1,7 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +endmenu + + diff --git a/arch/xtensa/Makefile b/arch/xtensa/Makefile new file mode 100644 index 000000000000..4fa27453b1f9 --- /dev/null +++ b/arch/xtensa/Makefile @@ -0,0 +1,102 @@ +# +# 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) 2001 - 2005 Tensilica Inc. +# +# 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 + +# Core configuration. +# (Use CPU= to use another default compiler.) + +cpu-$(CONFIG_XTENSA_CPU_LINUX_BE) := linux_be +cpu-$(CONFIG_XTENSA_CPU_LINUX_CUSTOM) := linux_custom + +CPU = $(cpu-y) +export CPU + +# Platform configuration + +platform-y := common +platform-$(CONFIG_XTENSA_PLATFORM_XT2000) := xt2000 +platform-$(CONFIG_XTENSA_PLATFORM_ISS) := iss + +PLATFORM = $(platform-y) +export PLATFORM + +#LDFLAGS_vmlinux := -T$(word 1,$(LINKSCRIPT)) +AFLAGS_vmlinux.lds.o := -Uxtensa +CPPFLAGS += -Iarch/xtensa -Iinclude/asm -mlongcalls -g +AFLAGS += -Iarch/xtensa -Iinclude/asm +CPP = $(CC) -E $(CFLAGS) + +cflags-y += -Iarch/xtensa -pipe -mlongcalls + + +KBUILD_DEFCONFIG := common_defconfig + +# ramdisk/initrd support +# You need a compressed ramdisk image, named ramdisk.gz in +# arch/xtensa/boot/ramdisk + +core-$(CONFIG_EMBEDDED_RAMDISK) += arch/xtensa/boot/ramdisk/ + +# Test for cross compiling + +ifneq ($(CPU),) + COMPILE_ARCH = $(shell uname -m) + + ifneq ($(COMPILE_ARCH), xtensa) + ifndef CROSS_COMPILE + CROSS_COMPILE = xtensa_$(CPU)- + endif + endif +endif + +# + +LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + +head-y := arch/xtensa/kernel/head.o +core-y += arch/xtensa/kernel/ \ + arch/xtensa/mm/ arch/xtensa/platform-$(PLATFORM)/ +libs-y += arch/xtensa/lib/ $(LIBGCC) + +boot := arch/xtensa/boot + +arch/xtensa/kernel/asm-offsets.s: \ + arch/xtensa/kernel/asm-offsets.c \ + include/asm-xtensa/.platform + +include/asm-xtensa/offsets.h: arch/xtensa/kernel/asm-offsets.s + $(call filechk,gen-asm-offsets) + +prepare: include/asm-xtensa/.platform include/asm-xtensa/offsets.h + +# Update machine cpu and platform symlinks if something which affects +# them changed. + +include/asm-xtensa/.platform: $(wildcard include/config/arch/*.h) + @echo ' Setting up cpu ($(CPU)) and platform ($(PLATFORM)) symlinks' + $(Q)rm -f include/asm-xtensa/platform + $(Q)rm -f include/asm-xtensa/xtensa/config + $(Q)(cd include/asm-xtensa/; ln -sf platform-$(PLATFORM) platform) + $(Q)(cd include/asm-xtensa/xtensa; ln -sf config-$(CPU) config) + +all: zImage + +bzImage : zImage + +zImage zImage.initrd: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $@ + +CLEAN_FILES += arch/xtensa/vmlinux.lds include/asm-xtensa/offset.h + +define archhelp + @echo '* zImage - Compressed kernel image (arch/xtensa/boot/images/zImage.*)' +endef + diff --git a/arch/xtensa/boot/Makefile b/arch/xtensa/boot/Makefile new file mode 100644 index 000000000000..260f456ccf0b --- /dev/null +++ b/arch/xtensa/boot/Makefile @@ -0,0 +1,37 @@ +# +# arch/xtensa/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. +# +# + + +CFLAGS += -fno-builtin -Iarch/$(ARCH)/boot/include +HOSTFLAGS += -Iarch/$(ARCH)/boot/include + +BIG_ENDIAN := $(shell echo -e "\#ifdef __XTENSA_EL__\nint little;\n\#else\nint big;\n\#endif" | $(CC) -E -|grep -c big) + + +export CFLAGS +export AFLAGS +export BIG_ENDIAN + +# Subdirs for the boot loader(s) + +bootdir-$(CONFIG_XTENSA_PLATFORM_ISS) += boot-elf +bootdir-$(CONFIG_XTENSA_PLATFORM_XT2000) += boot-redboot boot-elf + +subdir-y := lib/ + +subdir-y += boot-elf/ boot-redboot/ + +zImage zImage.initrd Image Image.initrd: $(bootdir-y) + +$(bootdir-y): $(addprefix $(obj)/,$(subdir-y)) \ + $(addprefix $(obj)/,$(host-progs)) + $(Q)$(MAKE) $(build)=$(obj)/$@ $(MAKECMDGOALS) + + + diff --git a/arch/xtensa/boot/boot-elf/Makefile b/arch/xtensa/boot/boot-elf/Makefile new file mode 100644 index 000000000000..f6ef6a369667 --- /dev/null +++ b/arch/xtensa/boot/boot-elf/Makefile @@ -0,0 +1,52 @@ +# +# 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. +# + +GZIP = gzip +GZIP_FLAGS = -v9fc + +ifeq ($(BIG_ENDIAN),1) +OBJCOPY_ARGS := -O elf32-xtensa-be +else +OBJCOPY_ARGS := -O elf32-xtensa-le +endif + +export OBJCOPY_ARGS + +boot-y := bootstrap.o + +OBJS := $(addprefix $(obj)/,$(boot-y)) + +Image: vmlinux $(OBJS) + $(OBJCOPY) --strip-all -R .comment -R .xt.insn -O binary \ + vmlinux vmlinux.tmp + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section image=vmlinux.tmp \ + --set-section-flags image=contents,alloc,load,load,data \ + $(OBJS) $@.tmp + $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) \ + -T arch/$(ARCH)/boot/boot-elf/boot.ld \ + -o arch/$(ARCH)/boot/$@.elf $@.tmp + rm -f $@.tmp vmlinux.tmp + +Image.initrd: vmlinux $(OBJS) + $(OBJCOPY) --strip-all -R .comment -R .xt.insn -O binary \ + --add-section .initrd=arch/$(ARCH)/boot/ramdisk \ + --set-section-flags .initrd=contents,alloc,load,load,data \ + vmlinux vmlinux.tmp + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section image=vmlinux.tmp \ + --set-section-flags image=contents,alloc,load,load,data \ + $(OBJS) $@.tmp + $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) \ + -T arch/$(ARCH)/boot/boot-elf/boot.ld \ + -o arch/$(ARCH)/boot/$@.elf $@.tmp + rm -f $@.tmp vmlinux.tmp + + +zImage: Image + +zImage.initrd: Image.initrd + diff --git a/arch/xtensa/boot/boot-elf/boot.ld b/arch/xtensa/boot/boot-elf/boot.ld new file mode 100644 index 000000000000..4ab06a0a7a6b --- /dev/null +++ b/arch/xtensa/boot/boot-elf/boot.ld @@ -0,0 +1,71 @@ +OUTPUT_ARCH(xtensa) + +SECTIONS +{ + .start 0xD0000000 : { *(.start) } + + .text 0xD0000000: + { + __reloc_start = . ; + _text_start = . ; + *(.literal .text.literal .text) + _text_end = . ; + } + + .rodata ALIGN(0x04): + { + *(.rodata) + *(.rodata1) + } + + .data ALIGN(0x04): + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) + *(.got) + *(.dynamic) + } + + __reloc_end = . ; + + .initrd ALIGN(0x10) : + { + boot_initrd_start = . ; + *(.initrd) + boot_initrd_end = .; + } + + . = ALIGN(0x10); + __image_load = . ; + .image 0xd0001000: + { + _image_start = .; + *(image) + . = (. + 3) & ~ 3; + _image_end = . ; + } + + + .bss ((LOADADDR(.image) + SIZEOF(.image) + 3) & ~ 3): + { + __bss_start = .; + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + __bss_end = .; + } + _end = .; + _param_start = .; + + .ResetVector.text 0xfe000020 : + { + *(.ResetVector.text) + } + + + PROVIDE (end = .); +} diff --git a/arch/xtensa/boot/boot-elf/bootstrap.S b/arch/xtensa/boot/boot-elf/bootstrap.S new file mode 100644 index 000000000000..7cba94abdab8 --- /dev/null +++ b/arch/xtensa/boot/boot-elf/bootstrap.S @@ -0,0 +1,37 @@ + +#include +#include + +#include +#include + + +/* ResetVector + */ + .section .ResetVector.text, "ax" + .global _ResetVector +_ResetVector: + _j reset + .align 4 +RomInitAddr: + .word 0xd0001000 +RomBootParam: + .word _bootparam +reset: + l32r a0, RomInitAddr + l32r a2, RomBootParam + movi a3, 0 + movi a4, 0 + jx a0 + + .align 4 + .section .bootstrap.data, "aw" + + .globl _bootparam +_bootparam: + .short BP_TAG_FIRST + .short 4 + .long BP_VERSION + .short BP_TAG_LAST + .short 0 + .long 0 diff --git a/arch/xtensa/boot/boot-redboot/Makefile b/arch/xtensa/boot/boot-redboot/Makefile new file mode 100644 index 000000000000..ca8a68bc8472 --- /dev/null +++ b/arch/xtensa/boot/boot-redboot/Makefile @@ -0,0 +1,35 @@ +# +# 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. +# + +GZIP = gzip +GZIP_FLAGS = -v9fc +ifeq ($(BIG_ENDIAN),1) +OBJCOPY_ARGS := -O elf32-xtensa-be +else +OBJCOPY_ARGS := -O elf32-xtensa-le +endif + +LD_ARGS = -T $(obj)/boot.ld + +boot-y := bootstrap.o + +OBJS := $(addprefix $(obj)/,$(boot-y)) +LIBS := arch/$(ARCH)/boot/lib/lib.a arch/$(ARCH)/lib/lib.a + +LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + +zImage: vmlinux $(OBJS) $(LIBS) + $(OBJCOPY) --strip-all -R .comment -R .xt.insn -O binary \ + $(TOPDIR)/vmlinux vmlinux.tmp + gzip -vf9 vmlinux.tmp + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section image=vmlinux.tmp.gz \ + --set-section-flags image=contents,alloc,load,load,data \ + $(OBJS) $@.tmp + $(LD) $(LD_ARGS) -o $@.elf $@.tmp $(LIBS) -L/xtensa-elf/lib $(LIBGCC) + $(OBJCOPY) -S -O binary $@.elf arch/$(ARCH)/boot/images/$@.redboot +# rm -f $@.tmp $@.elf vmlinux.tmp.gz + diff --git a/arch/xtensa/boot/boot-redboot/boot.ld b/arch/xtensa/boot/boot-redboot/boot.ld new file mode 100644 index 000000000000..65b726410e8a --- /dev/null +++ b/arch/xtensa/boot/boot-redboot/boot.ld @@ -0,0 +1,66 @@ +OUTPUT_ARCH(xtensa) + +SECTIONS +{ + .start 0xD0200000 : { *(.start) } + + .text : + { + __reloc_start = . ; + _text_start = . ; + *(.literal .text.literal .text) + _text_end = . ; + } + + .rodata ALIGN(0x04): + { + *(.rodata) + *(.rodata1) + } + + .data ALIGN(0x04): + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) + *(.got) + *(.dynamic) + } + + __reloc_end = . ; + + .initrd ALIGN(0x10) : + { + boot_initrd_start = . ; + *(.initrd) + boot_initrd_end = .; + } + + . = ALIGN(0x10); + __image_load = . ; + .image 0xd0001000: AT(__image_load) + { + _image_start = .; + *(image) + . = (. + 3) & ~ 3; + _image_end = . ; + } + + + .bss ((LOADADDR(.image) + SIZEOF(.image) + 3) & ~ 3): + { + __bss_start = .; + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + __bss_end = .; + } + _end = .; + _param_start = .; + + + PROVIDE (end = .); +} diff --git a/arch/xtensa/boot/boot-redboot/bootstrap.S b/arch/xtensa/boot/boot-redboot/bootstrap.S new file mode 100644 index 000000000000..ee636b0da81c --- /dev/null +++ b/arch/xtensa/boot/boot-redboot/bootstrap.S @@ -0,0 +1,246 @@ + +#define _ASMLANGUAGE +#include +#include +#include + + /* + * RB-Data: RedBoot data/bss + * P: Boot-Parameters + * L: Kernel-Loader + * + * The Linux-Kernel image including the loader must be loaded + * to a position so that the kernel and the boot parameters + * can fit in the space before the load address. + * ______________________________________________________ + * |_RB-Data_|_P_|__________|_L_|___Linux-Kernel___|______| + * ^ + * ^ Load address + * ______________________________________________________ + * |___Linux-Kernel___|_P_|_L_|___________________________| + * + * The loader copies the parameter to the position that will + * be the end of the kernel and itself to the end of the + * parameter list. + */ + +/* Make sure we have enough space for the 'uncompressor' */ + +#define STACK_SIZE 32768 +#define HEAP_SIZE (131072*4) + + # a2: Parameter list + # a3: Size of parameter list + + .section .start, "ax" + + .globl __start + /* this must be the first byte of the loader! */ +__start: + entry sp, 32 # we do not intend to return + _call0 _start +__start_a0: + .align 4 + + .section .text, "ax" + .begin literal_prefix .text + + /* put literals in here! */ + + .globl _start +_start: + + /* 'reset' window registers */ + + movi a4, 1 + wsr a4, PS + rsync + + rsr a5, WINDOWBASE + ssl a5 + sll a4, a4 + wsr a4, WINDOWSTART + rsync + + movi a4, 0x00040000 + wsr a4, PS + rsync + + /* copy the loader to its address + * Note: The loader itself is a very small piece, so we assume we + * don't partially overlap. We also assume (even more important) + * that the kernel image is out of the way. Usually, when the + * load address of this image is not at an arbitrary address, + * but aligned to some 10K's we shouldn't overlap. + */ + + /* Note: The assembler cannot relax "addi a0, a0, ..." to an + l32r, so we load to a4 first. */ + + addi a4, a0, __start - __start_a0 + mov a0, a4 + movi a4, __start + movi a5, __reloc_end + + # a0: address where this code has been loaded + # a4: compiled address of __start + # a5: compiled end address + + mov.n a7, a0 + mov.n a8, a4 + +1: + l32i a10, a7, 0 + l32i a11, a7, 4 + s32i a10, a8, 0 + s32i a11, a8, 4 + l32i a10, a7, 8 + l32i a11, a7, 12 + s32i a10, a8, 8 + s32i a11, a8, 12 + addi a8, a8, 16 + addi a7, a7, 16 + blt a8, a5, 1b + + + /* We have to flush and invalidate the caches here before we jump. */ + +#if XCHAL_DCACHE_IS_WRITEBACK + dcache_writeback_all a5, a6 +#endif + icache_invalidate_all a5, a6 + + movi a11, _reloc + jx a11 + + .globl _reloc +_reloc: + + /* RedBoot is now at the end of the memory, so we don't have + * to copy the parameter list. Keep the code around; in case + * we need it again. */ +#if 0 + # a0: load address + # a2: start address of parameter list + # a3: length of parameter list + # a4: __start + + /* copy the parameter list out of the way */ + + movi a6, _param_start + add a3, a2, a3 +2: + l32i a8, a2, 0 + s32i a8, a6, 0 + addi a2, a2, 4 + addi a6, a6, 4 + blt a2, a3, 2b +#endif + + /* clear BSS section */ + movi a6, __bss_start + movi a7, __bss_end + movi.n a5, 0 +3: + s32i a5, a6, 0 + addi a6, a6, 4 + blt a6, a7, 3b + + movi a5, -16 + movi a1, _stack + STACK_SIZE + and a1, a1, a5 + + /* Uncompress the kernel */ + + # a0: load address + # a2: boot parameter + # a4: __start + + movi a3, __image_load + sub a4, a3, a4 + add a8, a0, a4 + + # a1 Stack + # a8(a4) Load address of the image + + movi a6, _image_start + movi a10, _image_end + movi a7, 0x1000000 + sub a11, a10, a6 + movi a9, complen + s32i a11, a9, 0 + + movi a0, 0 + + # a6 destination + # a7 maximum size of destination + # a8 source + # a9 ptr to length + + .extern gunzip + movi a4, gunzip + beqz a4, 1f + + callx4 a4 + + j 2f + + + # a6 destination start + # a7 maximum size of destination + # a8 source start + # a9 ptr to length + # a10 destination end + +1: + l32i a9, a8, 0 + l32i a11, a8, 4 + s32i a9, a6, 0 + s32i a11, a6, 4 + l32i a9, a8, 8 + l32i a11, a8, 12 + s32i a9, a6, 8 + s32i a11, a6, 12 + addi a6, a6, 16 + addi a8, a8, 16 + blt a6, a10, 1b + + + /* jump to the kernel */ +2: +#if XCHAL_DCACHE_IS_WRITEBACK + dcache_writeback_all a5, a6 +#endif + icache_invalidate_all a5, a6 + + movi a5, __start + movi a3, boot_initrd_start + movi a4, boot_initrd_end + sub a3, a3, a5 + sub a4, a4, a5 + add a3, a0, a3 + add a4, a0, a4 + + # a2 Boot parameter list + # a3 initrd_start (virtual load address) + # a4 initrd_end (virtual load address) + + movi a0, _image_start + jx a0 + + .align 16 + .data + .globl avail_ram +avail_ram: + .long _heap + .globl end_avail +end_avail: + .long _heap + HEAP_SIZE + + .comm _stack, STACK_SIZE + .comm _heap, HEAP_SIZE + + .globl end_avail + .comm complen, 4 + + .end literal_prefix diff --git a/arch/xtensa/boot/include/zlib.h b/arch/xtensa/boot/include/zlib.h new file mode 100644 index 000000000000..ea29b6237852 --- /dev/null +++ b/arch/xtensa/boot/include/zlib.h @@ -0,0 +1,433 @@ +/* + * BK Id: SCCS/s.zlib.h 1.8 05/18/01 15:17:23 cort + */ +/* + * 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< + +.text +.align 4 +.global bcopy +.type bcopy,@function +bcopy: + movi a14, xthal_bcopy // a14 safe to use regardless of whether caller + // used call4 or call8 (can't have used call12) + jx a14 // let the Core HAL do the work + +.text +.align 4 +.global memcpy +.type memcpy,@function +memcpy: +.global memmove +.type memmove,@function +memmove: + movi a14, xthal_memcpy // a14 safe to use regardless of whether caller + // used call4 or call8 (can't have used call12) + jx a14 // let the Core HAL do the work + diff --git a/arch/xtensa/boot/lib/zlib.c b/arch/xtensa/boot/lib/zlib.c new file mode 100644 index 000000000000..e3859f631077 --- /dev/null +++ b/arch/xtensa/boot/lib/zlib.c @@ -0,0 +1,2150 @@ +/* + * BK Id: SCCS/s.zlib.c 1.8 05/18/01 15:17:24 cort + */ +/* + * 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 + * + */ + +/*+++++*/ +/* 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 --git a/arch/xtensa/boot/lib/zmem.c b/arch/xtensa/boot/lib/zmem.c new file mode 100644 index 000000000000..7848f126d67d --- /dev/null +++ b/arch/xtensa/boot/lib/zmem.c @@ -0,0 +1,87 @@ +#include "zlib.h" + +/* bits taken from ppc */ + +extern void *avail_ram, *end_avail; + +void exit (void) +{ + for (;;); +} + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p = avail_ram; + + size *= items; + size = (size + 7) & -8; + avail_ram += size; + if (avail_ram > end_avail) { + //puts("oops... out of memory\n"); + //pause(); + exit (); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ +} + + +#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) { + //puts("bad gzipped data\n"); + 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) { + //puts("gunzip: ran out of data in header\n"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + //puts("inflateInit2 returned "); puthex(r); puts("\n"); + 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) { + //puts("inflate returned "); puthex(r); puts("\n"); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} + diff --git a/arch/xtensa/boot/ramdisk/Makefile b/arch/xtensa/boot/ramdisk/Makefile new file mode 100644 index 000000000000..b12f76352438 --- /dev/null +++ b/arch/xtensa/boot/ramdisk/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for a ramdisk image +# + +BIG_ENDIAN := $(shell echo -e "\#ifdef __XTENSA_EL__\nint little;\n\#else\nint big;\n\#endif" | $(CC) -E -|grep -c big) + +ifeq ($(BIG_ENDIAN),1) +OBJCOPY_ARGS := -O elf32-xtensa-be +else +OBJCOPY_ARGS := -O elf32-xtensa-le +endif + +obj-y = ramdisk.o + +RAMDISK_IMAGE = arch/$(ARCH)/boot/ramdisk/$(CONFIG_EMBEDDED_RAMDISK_IMAGE) + +arch/$(ARCH)/boot/ramdisk/ramdisk.o: + $(Q)echo -e "dummy:" | $(AS) -o $@; + $(Q)$(OBJCOPY) $(OBJCOPY_ARGS) \ + --add-section .initrd=$(RAMDISK_IMAGE) \ + --set-section-flags .initrd=contents,alloc,load,load,data \ + arch/$(ARCH)/boot/ramdisk/ramdisk.o $@ + diff --git a/arch/xtensa/configs/common_defconfig b/arch/xtensa/configs/common_defconfig new file mode 100644 index 000000000000..1d230ee081b4 --- /dev/null +++ b/arch/xtensa/configs/common_defconfig @@ -0,0 +1,662 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.11-rc2 +# Tue Mar 1 16:36:53 2005 +# +# CONFIG_FRAME_POINTER is not set +CONFIG_XTENSA=y +# CONFIG_UID16 is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y +CONFIG_GENERIC_HARDIRQS=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODULE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_XTENSA_ARCH_LINUX_BE=y +# CONFIG_XTENSA_ARCH_LINUX_LE is not set +# CONFIG_XTENSA_ARCH_LINUX_TEST is not set +# CONFIG_XTENSA_ARCH_S5 is not set +# CONFIG_XTENSA_CUSTOM is not set +CONFIG_MMU=y +# CONFIG_XTENSA_UNALIGNED_USER is not set +# CONFIG_PREEMPT is not set +# CONFIG_MATH_EMULATION is not set +# CONFIG_HIGHMEM is not set + +# +# Platform options +# +# CONFIG_XTENSA_PLATFORM_ISS is not set +CONFIG_XTENSA_PLATFORM_XT2000=y +CONFIG_XTENSA_CALIBRATE_CCOUNT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0,38400 ip=bootp root=nfs nfsroot=/opt/montavista/pro/devkit/xtensa/linux_be/target" + +# +# Bus options +# +CONFIG_PCI=y +# CONFIG_PCI_LEGACY_PROC is not set +# CONFIG_PCI_NAMES is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# Exectuable file formats +# +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD 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_UMEM is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_IP_TCPDIAG is not set +# CONFIG_IP_TCPDIAG_IPV6 is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +# CONFIG_NET_SCH_HFSC is not set +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +# CONFIG_NET_SCH_NETEM is not set +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +# CONFIG_CLS_U32_PERF is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MII is not set +CONFIG_XT2000_SONIC=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set +# CONFIG_NET_PCI is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +CONFIG_STRIP=m + +# +# Wireless 802.11b ISA/PCI cards support +# +CONFIG_HERMES=m +# CONFIG_PLX_HERMES is not set +# CONFIG_TMD_HERMES is not set +# CONFIG_PCI_HERMES is not set +# CONFIG_ATMEL is not set + +# +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support +# +# CONFIG_PRISM54 is not set +CONFIG_NET_WIRELESS=y + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# InfiniBand support +# +# CONFIG_INFINIBAND is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_SLAB is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_KGDB is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC32 is not set +# CONFIG_LIBCRC32C is not set diff --git a/arch/xtensa/configs/iss_defconfig b/arch/xtensa/configs/iss_defconfig new file mode 100644 index 000000000000..802621dd4867 --- /dev/null +++ b/arch/xtensa/configs/iss_defconfig @@ -0,0 +1,531 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.11-rc2 +# Fri Feb 25 19:21:24 2005 +# +CONFIG_FRAME_POINTER=y +CONFIG_XTENSA=y +# CONFIG_UID16 is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y +CONFIG_GENERIC_HARDIRQS=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_KOBJECT_UEVENT is not set +# CONFIG_IKCONFIG is not set +CONFIG_EMBEDDED=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# Processor type and features +# +CONFIG_XTENSA_ARCH_LINUX_BE=y +# CONFIG_XTENSA_ARCH_LINUX_LE is not set +# CONFIG_XTENSA_ARCH_LINUX_TEST is not set +# CONFIG_XTENSA_ARCH_S5 is not set +# CONFIG_XTENSA_CUSTOM is not set +CONFIG_MMU=y +# CONFIG_XTENSA_UNALIGNED_USER is not set +# CONFIG_PREEMPT is not set +# CONFIG_MATH_EMULATION is not set +# CONFIG_HIGHMEM is not set + +# +# Platform options +# +CONFIG_XTENSA_PLATFORM_ISS=y +# CONFIG_XTENSA_PLATFORM_XT2000 is not set +# CONFIG_XTENSA_PLATFORM_ARUBA is not set +# CONFIG_XTENSA_CALIBRATE_CCOUNT is not set +CONFIG_XTENSA_CPU_CLOCK=10 +# CONFIG_GENERIC_CALIBRATE_DELAY is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0,38400 eth0=tuntap,,tap0 ip=192.168.168.5:192.168.168.1 root=nfs nfsroot=192.168.168.1:/opt/montavista/pro/devkit/xtensa/linux_be/target" +CONFIG_SERIAL_CONSOLE=y +CONFIG_XTENSA_ISS_NETWORK=y + +# +# Bus options +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# + +# +# PCI Hotplug Support +# + +# +# Exectuable file formats +# +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_IP_TCPDIAG is not set +# CONFIG_IP_TCPDIAG_IPV6 is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +# CONFIG_SCTP_HMAC_MD5 is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_SCH_CLK_JIFFIES is not set +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +# CONFIG_SERIO is not set +# CONFIG_SERIO_I8042 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +CONFIG_SOFT_WATCHDOG=y +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# InfiniBand support +# +# CONFIG_INFINIBAND is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_DNOTIFY is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +CONFIG_NFS_DIRECTIO=y +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_KGDB is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC32 is not set +# CONFIG_LIBCRC32C is not set diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile new file mode 100644 index 000000000000..d573017a5dde --- /dev/null +++ b/arch/xtensa/kernel/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the Linux/Xtensa kernel. +# + +extra-y := head.o vmlinux.lds + + +obj-y := align.o entry.o irq.o coprocessor.o process.o ptrace.o semaphore.o \ + setup.o signal.o syscalls.o time.o traps.o vectors.o platform.o \ + pci-dma.o + +## windowspill.o + +obj-$(CONFIG_KGDB) += xtensa-stub.o +obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o + + diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S new file mode 100644 index 000000000000..74b1e90ef08c --- /dev/null +++ b/arch/xtensa/kernel/align.S @@ -0,0 +1,459 @@ +/* + * arch/xtensa/kernel/align.S + * + * Handle unalignment exceptions in kernel space. + * + * 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) 2001 - 2005 Tensilica, Inc. + * + * Rewritten by Chris Zankel + * + * Based on work from Joe Taylor + * and Marc Gauthier + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION + +/* First-level exception handler for unaligned exceptions. + * + * Note: This handler works only for kernel exceptions. Unaligned user + * access should get a seg fault. + */ + +/* Big and little endian 16-bit values are located in + * different halves of a register. HWORD_START helps to + * abstract the notion of extracting a 16-bit value from a + * register. + * We also have to define new shifting instructions because + * lsb and msb are on 'opposite' ends in a register for + * different endian machines. + * + * Assume a memory region in ascending address: + * 0 1 2 3|4 5 6 7 + * + * When loading one word into a register, the content of that register is: + * LE 3 2 1 0, 7 6 5 4 + * BE 0 1 2 3, 4 5 6 7 + * + * Masking the bits of the higher/lower address means: + * LE X X 0 0, 0 0 X X + * BE 0 0 X X, X X 0 0 + * + * Shifting to higher/lower addresses, means: + * LE shift left / shift right + * BE shift right / shift left + * + * Extracting 16 bits from a 32 bit reg. value to higher/lower address means: + * LE mask 0 0 X X / shift left + * BE shift left / mask 0 0 X X + */ + +#define UNALIGNED_USER_EXCEPTION + +#if XCHAL_HAVE_BE + +#define HWORD_START 16 +#define INSN_OP0 28 +#define INSN_T 24 +#define INSN_OP1 16 + +.macro __src_b r, w0, w1; src \r, \w0, \w1; .endm +.macro __ssa8 r; ssa8b \r; .endm +.macro __ssa8r r; ssa8l \r; .endm +.macro __sh r, s; srl \r, \s; .endm +.macro __sl r, s; sll \r, \s; .endm +.macro __exth r, s; extui \r, \s, 0, 16; .endm +.macro __extl r, s; slli \r, \s, 16; .endm + +#else + +#define HWORD_START 0 +#define INSN_OP0 0 +#define INSN_T 4 +#define INSN_OP1 12 + +.macro __src_b r, w0, w1; src \r, \w1, \w0; .endm +.macro __ssa8 r; ssa8l \r; .endm +.macro __ssa8r r; ssa8b \r; .endm +.macro __sh r, s; sll \r, \s; .endm +.macro __sl r, s; srl \r, \s; .endm +.macro __exth r, s; slli \r, \s, 16; .endm +.macro __extl r, s; extui \r, \s, 0, 16; .endm + +#endif + +/* + * xxxx xxxx = imm8 field + * yyyy = imm4 field + * ssss = s field + * tttt = t field + * + * 16 0 + * ------------------- + * L32I.N yyyy ssss tttt 1000 + * S32I.N yyyy ssss tttt 1001 + * + * 23 0 + * ----------------------------- + * res 0000 0010 + * L16UI xxxx xxxx 0001 ssss tttt 0010 + * L32I xxxx xxxx 0010 ssss tttt 0010 + * XXX 0011 ssss tttt 0010 + * XXX 0100 ssss tttt 0010 + * S16I xxxx xxxx 0101 ssss tttt 0010 + * S32I xxxx xxxx 0110 ssss tttt 0010 + * XXX 0111 ssss tttt 0010 + * XXX 1000 ssss tttt 0010 + * L16SI xxxx xxxx 1001 ssss tttt 0010 + * XXX 1010 0010 + * **L32AI xxxx xxxx 1011 ssss tttt 0010 unsupported + * XXX 1100 0010 + * XXX 1101 0010 + * XXX 1110 0010 + * **S32RI xxxx xxxx 1111 ssss tttt 0010 unsupported + * ----------------------------- + * ^ ^ ^ + * sub-opcode (NIBBLE_R) -+ | | + * t field (NIBBLE_T) -----------+ | + * major opcode (NIBBLE_OP0) --------------+ + */ + +#define OP0_L32I_N 0x8 /* load immediate narrow */ +#define OP0_S32I_N 0x9 /* store immediate narrow */ +#define OP1_SI_MASK 0x4 /* OP1 bit set for stores */ +#define OP1_SI_BIT 2 /* OP1 bit number for stores */ + +#define OP1_L32I 0x2 +#define OP1_L16UI 0x1 +#define OP1_L16SI 0x9 +#define OP1_L32AI 0xb + +#define OP1_S32I 0x6 +#define OP1_S16I 0x5 +#define OP1_S32RI 0xf + +/* + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + */ + + +ENTRY(fast_unaligned) + + /* Note: We don't expect the address to be aligned on a word + * boundary. After all, the processor generated that exception + * and it would be a hardware fault. + */ + + /* Save some working register */ + + s32i a4, a2, PT_AREG4 + s32i a5, a2, PT_AREG5 + s32i a6, a2, PT_AREG6 + s32i a7, a2, PT_AREG7 + s32i a8, a2, PT_AREG8 + + rsr a0, DEPC + xsr a3, EXCSAVE_1 + s32i a0, a2, PT_AREG2 + s32i a3, a2, PT_AREG3 + + /* Keep value of SAR in a0 */ + + rsr a0, SAR + rsr a8, EXCVADDR # load unaligned memory address + + /* Now, identify one of the following load/store instructions. + * + * The only possible danger of a double exception on the + * following l32i instructions is kernel code in vmalloc + * memory. The processor was just executing at the EPC_1 + * address, and indeed, already fetched the instruction. That + * guarantees a TLB mapping, which hasn't been replaced by + * this unaligned exception handler that uses only static TLB + * mappings. However, high-level interrupt handlers might + * modify TLB entries, so for the generic case, we register a + * TABLE_FIXUP handler here, too. + */ + + /* a3...a6 saved on stack, a2 = SP */ + + /* Extract the instruction that caused the unaligned access. */ + + rsr a7, EPC_1 # load exception address + movi a3, ~3 + and a3, a3, a7 # mask lower bits + + l32i a4, a3, 0 # load 2 words + l32i a5, a3, 4 + + __ssa8 a7 + __src_b a4, a4, a5 # a4 has the instruction + + /* Analyze the instruction (load or store?). */ + + extui a5, a4, INSN_OP0, 4 # get insn.op0 nibble + +#if XCHAL_HAVE_NARROW + _beqi a5, OP0_L32I_N, .Lload # L32I.N, jump + addi a6, a5, -OP0_S32I_N + _beqz a6, .Lstore # S32I.N, do a store +#endif + /* 'store indicator bit' not set, jump */ + _bbci.l a4, OP1_SI_BIT + INSN_OP1, .Lload + + /* Store: Jump to table entry to get the value in the source register.*/ + +.Lstore:movi a5, .Lstore_table # table + extui a6, a4, INSN_T, 4 # get source register + addx8 a5, a6, a5 + jx a5 # jump into table + + /* Invalid instruction, CRITICAL! */ +.Linvalid_instruction_load: + j .Linvalid_instruction + + /* Load: Load memory address. */ + +.Lload: movi a3, ~3 + and a3, a3, a8 # align memory address + + __ssa8 a8 +#ifdef UNALIGNED_USER_EXCEPTION + addi a3, a3, 8 + l32e a5, a3, -8 + l32e a6, a3, -4 +#else + l32i a5, a3, 0 + l32i a6, a3, 4 +#endif + __src_b a3, a5, a6 # a3 has the data word + +#if XCHAL_HAVE_NARROW + addi a7, a7, 2 # increment PC (assume 16-bit insn) + + extui a5, a4, INSN_OP0, 4 + _beqi a5, OP0_L32I_N, 1f # l32i.n: jump + + addi a7, a7, 1 +#else + addi a7, a7, 3 +#endif + + extui a5, a4, INSN_OP1, 4 + _beqi a5, OP1_L32I, 1f # l32i: jump + + extui a3, a3, 0, 16 # extract lower 16 bits + _beqi a5, OP1_L16UI, 1f + addi a5, a5, -OP1_L16SI + _bnez a5, .Linvalid_instruction_load + + /* sign extend value */ + + slli a3, a3, 16 + srai a3, a3, 16 + + /* Set target register. */ + +1: + +#if XCHAL_HAVE_LOOP + rsr a3, LEND # check if we reached LEND + bne a7, a3, 1f + rsr a3, LCOUNT # and LCOUNT != 0 + beqz a3, 1f + addi a3, a3, -1 # decrement LCOUNT and set + rsr a7, LBEG # set PC to LBEGIN + wsr a3, LCOUNT +#endif + +1: wsr a7, EPC_1 # skip load instruction + extui a4, a4, INSN_T, 4 # extract target register + movi a5, .Lload_table + addx8 a4, a4, a5 + jx a4 # jump to entry for target register + + .align 8 +.Lload_table: + s32i a3, a2, PT_AREG0; _j .Lexit; .align 8 + mov a1, a3; _j .Lexit; .align 8 # fishy?? + s32i a3, a2, PT_AREG2; _j .Lexit; .align 8 + s32i a3, a2, PT_AREG3; _j .Lexit; .align 8 + s32i a3, a2, PT_AREG4; _j .Lexit; .align 8 + s32i a3, a2, PT_AREG5; _j .Lexit; .align 8 + s32i a3, a2, PT_AREG6; _j .Lexit; .align 8 + s32i a3, a2, PT_AREG7; _j .Lexit; .align 8 + s32i a3, a2, PT_AREG8; _j .Lexit; .align 8 + mov a9, a3 ; _j .Lexit; .align 8 + mov a10, a3 ; _j .Lexit; .align 8 + mov a11, a3 ; _j .Lexit; .align 8 + mov a12, a3 ; _j .Lexit; .align 8 + mov a13, a3 ; _j .Lexit; .align 8 + mov a14, a3 ; _j .Lexit; .align 8 + mov a15, a3 ; _j .Lexit; .align 8 + +.Lstore_table: + l32i a3, a2, PT_AREG0; _j 1f; .align 8 + mov a3, a1; _j 1f; .align 8 # fishy?? + l32i a3, a2, PT_AREG2; _j 1f; .align 8 + l32i a3, a2, PT_AREG3; _j 1f; .align 8 + l32i a3, a2, PT_AREG4; _j 1f; .align 8 + l32i a3, a2, PT_AREG5; _j 1f; .align 8 + l32i a3, a2, PT_AREG6; _j 1f; .align 8 + l32i a3, a2, PT_AREG7; _j 1f; .align 8 + l32i a3, a2, PT_AREG8; _j 1f; .align 8 + mov a3, a9 ; _j 1f; .align 8 + mov a3, a10 ; _j 1f; .align 8 + mov a3, a11 ; _j 1f; .align 8 + mov a3, a12 ; _j 1f; .align 8 + mov a3, a13 ; _j 1f; .align 8 + mov a3, a14 ; _j 1f; .align 8 + mov a3, a15 ; _j 1f; .align 8 + +1: # a7: instruction pointer, a4: instruction, a3: value + + movi a6, 0 # mask: ffffffff:00000000 + +#if XCHAL_HAVE_NARROW + addi a7, a7, 2 # incr. PC,assume 16-bit instruction + + extui a5, a4, INSN_OP0, 4 # extract OP0 + addi a5, a5, -OP0_S32I_N + _beqz a5, 1f # s32i.n: jump + + addi a7, a7, 1 # increment PC, 32-bit instruction +#else + addi a7, a7, 3 # increment PC, 32-bit instruction +#endif + + extui a5, a4, INSN_OP1, 4 # extract OP1 + _beqi a5, OP1_S32I, 1f # jump if 32 bit store + _bnei a5, OP1_S16I, .Linvalid_instruction_store + + movi a5, -1 + __extl a3, a3 # get 16-bit value + __exth a6, a5 # get 16-bit mask ffffffff:ffff0000 + + /* Get memory address */ + +1: +#if XCHAL_HAVE_LOOP + rsr a3, LEND # check if we reached LEND + bne a7, a3, 1f + rsr a3, LCOUNT # and LCOUNT != 0 + beqz a3, 1f + addi a3, a3, -1 # decrement LCOUNT and set + rsr a7, LBEG # set PC to LBEGIN + wsr a3, LCOUNT +#endif + +1: wsr a7, EPC_1 # skip store instruction + movi a4, ~3 + and a4, a4, a8 # align memory address + + /* Insert value into memory */ + + movi a5, -1 # mask: ffffffff:XXXX0000 +#ifdef UNALIGNED_USER_EXCEPTION + addi a4, a4, 8 +#endif + + __ssa8r a8 + __src_b a7, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE) + __src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE) +#ifdef UNALIGNED_USER_EXCEPTION + l32e a5, a4, -8 +#else + l32i a5, a4, 0 # load lower address word +#endif + and a5, a5, a7 # mask + __sh a7, a3 # shift value + or a5, a5, a7 # or with original value +#ifdef UNALIGNED_USER_EXCEPTION + s32e a5, a4, -8 + l32e a7, a4, -4 +#else + s32i a5, a4, 0 # store + l32i a7, a4, 4 # same for upper address word +#endif + __sl a5, a3 + and a6, a7, a6 + or a6, a6, a5 +#ifdef UNALIGNED_USER_EXCEPTION + s32e a6, a4, -4 +#else + s32i a6, a4, 4 +#endif + + /* Done. restore stack and return */ + +.Lexit: + movi a4, 0 + rsr a3, EXCSAVE_1 + s32i a4, a3, EXC_TABLE_FIXUP + + /* Restore working register */ + + l32i a7, a2, PT_AREG7 + l32i a6, a2, PT_AREG6 + l32i a5, a2, PT_AREG5 + l32i a4, a2, PT_AREG4 + l32i a3, a2, PT_AREG3 + + /* restore SAR and return */ + + wsr a0, SAR + l32i a0, a2, PT_AREG0 + l32i a2, a2, PT_AREG2 + rfe + + /* We cannot handle this exception. */ + + .extern _kernel_exception +.Linvalid_instruction_store: +.Linvalid_instruction: + + /* Restore a4...a8 and SAR, set SP, and jump to default exception. */ + + l32i a8, a2, PT_AREG8 + l32i a7, a2, PT_AREG7 + l32i a6, a2, PT_AREG6 + l32i a5, a2, PT_AREG5 + l32i a4, a2, PT_AREG4 + wsr a0, SAR + mov a1, a2 + + rsr a0, PS + bbsi.l a2, PS_UM_SHIFT, 1f # jump if user mode + + movi a0, _kernel_exception + jx a0 + +1: movi a0, _user_exception + jx a0 + + +#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */ + diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c new file mode 100644 index 000000000000..840cd9a1d3d2 --- /dev/null +++ b/arch/xtensa/kernel/asm-offsets.c @@ -0,0 +1,94 @@ +/* + * arch/xtensa/kernel/asm-offsets.c + * + * Generates definitions from c-type structures used by assembly sources. + * + * 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) 2005 Tensilica Inc. + * + * Chris Zankel + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFINE(sym, val) asm volatile("\n->" #sym " %0 " #val : : "i" (val)) +#define BLANK() asm volatile("\n->" : : ) + +int main(void) +{ + /* struct pt_regs */ + DEFINE(PT_PC, offsetof (struct pt_regs, pc)); + DEFINE(PT_PS, offsetof (struct pt_regs, ps)); + DEFINE(PT_DEPC, offsetof (struct pt_regs, depc)); + DEFINE(PT_EXCCAUSE, offsetof (struct pt_regs, exccause)); + DEFINE(PT_EXCVADDR, offsetof (struct pt_regs, excvaddr)); + DEFINE(PT_DEBUGCAUSE, offsetof (struct pt_regs, debugcause)); + DEFINE(PT_WMASK, offsetof (struct pt_regs, wmask)); + DEFINE(PT_LBEG, offsetof (struct pt_regs, lbeg)); + DEFINE(PT_LEND, offsetof (struct pt_regs, lend)); + DEFINE(PT_LCOUNT, offsetof (struct pt_regs, lcount)); + DEFINE(PT_SAR, offsetof (struct pt_regs, sar)); + DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall)); + DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0])); + DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0])); + DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1])); + DEFINE(PT_AREG2, offsetof (struct pt_regs, areg[2])); + DEFINE(PT_AREG3, offsetof (struct pt_regs, areg[3])); + DEFINE(PT_AREG4, offsetof (struct pt_regs, areg[4])); + DEFINE(PT_AREG5, offsetof (struct pt_regs, areg[5])); + DEFINE(PT_AREG6, offsetof (struct pt_regs, areg[6])); + DEFINE(PT_AREG7, offsetof (struct pt_regs, areg[7])); + DEFINE(PT_AREG8, offsetof (struct pt_regs, areg[8])); + DEFINE(PT_AREG9, offsetof (struct pt_regs, areg[9])); + DEFINE(PT_AREG10, offsetof (struct pt_regs, areg[10])); + DEFINE(PT_AREG11, offsetof (struct pt_regs, areg[11])); + DEFINE(PT_AREG12, offsetof (struct pt_regs, areg[12])); + DEFINE(PT_AREG13, offsetof (struct pt_regs, areg[13])); + DEFINE(PT_AREG14, offsetof (struct pt_regs, areg[14])); + DEFINE(PT_AREG15, offsetof (struct pt_regs, areg[15])); + DEFINE(PT_WINDOWBASE, offsetof (struct pt_regs, windowbase)); + DEFINE(PT_WINDOWSTART, offsetof(struct pt_regs, windowstart)); + DEFINE(PT_SIZE, sizeof(struct pt_regs)); + DEFINE(PT_AREG_END, offsetof (struct pt_regs, areg[XCHAL_NUM_AREGS])); + DEFINE(PT_USER_SIZE, offsetof(struct pt_regs, areg[XCHAL_NUM_AREGS])); + BLANK(); + + /* struct task_struct */ + DEFINE(TASK_PTRACE, offsetof (struct task_struct, ptrace)); + DEFINE(TASK_MM, offsetof (struct task_struct, mm)); + DEFINE(TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm)); + DEFINE(TASK_PID, offsetof (struct task_struct, pid)); + DEFINE(TASK_THREAD, offsetof (struct task_struct, thread)); + DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, thread_info)); + DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct)); + BLANK(); + + /* struct thread_info (offset from start_struct) */ + DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra)); + DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp)); + DEFINE(THREAD_CP_SAVE, offsetof (struct task_struct, thread.cp_save)); + DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds)); + BLANK(); + + /* struct mm_struct */ + DEFINE(MM_USERS, offsetof(struct mm_struct, mm_users)); + DEFINE(MM_PGD, offsetof (struct mm_struct, pgd)); + DEFINE(MM_CONTEXT, offsetof (struct mm_struct, context)); + BLANK(); + DEFINE(PT_SINGLESTEP_BIT, PT_SINGLESTEP_BIT); + return 0; +} + + diff --git a/arch/xtensa/kernel/coprocessor.S b/arch/xtensa/kernel/coprocessor.S new file mode 100644 index 000000000000..356192a4d39d --- /dev/null +++ b/arch/xtensa/kernel/coprocessor.S @@ -0,0 +1,201 @@ +/* + * arch/xtensa/kernel/coprocessor.S + * + * Xtensa processor configuration-specific table of coprocessor and + * other custom register layout information. + * + * 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) 2003 - 2005 Tensilica Inc. + * + * Marc Gauthier + */ + +/* + * This module contains a table that describes the layout of the various + * custom registers and states associated with each coprocessor, as well + * as those not associated with any coprocessor ("extra state"). + * This table is included with core dumps and is available via the ptrace + * interface, allowing the layout of such register/state information to + * be modified in the kernel without affecting the debugger. Each + * register or state is identified using a 32-bit "libdb target number" + * assigned when the Xtensa processor is generated. + */ + +#include +#include +#include + +#if XCHAL_HAVE_CP + +#define CP_LAST ((XCHAL_CP_MAX - 1) * COPROCESSOR_INFO_SIZE) + +ENTRY(release_coprocessors) + + entry a1, 16 + # a2: task + movi a3, 1 << XCHAL_CP_MAX # a3: coprocessor-bit + movi a4, coprocessor_info+CP_LAST # a4: owner-table + # a5: tmp + movi a6, 0 # a6: 0 + rsil a7, LOCKLEVEL # a7: PS + +1: /* Check if task is coprocessor owner of coprocessor[i]. */ + + l32i a5, a4, COPROCESSOR_INFO_OWNER + srli a3, a3, 1 + beqz a3, 1f + addi a4, a4, -8 + beq a2, a5, 1b + + /* Found an entry: Clear entry CPENABLE bit to disable CP. */ + + rsr a5, CPENABLE + s32i a6, a4, COPROCESSOR_INFO_OWNER + xor a5, a3, a5 + wsr a5, CPENABLE + + bnez a3, 1b + +1: wsr a7, PS + rsync + retw + + +ENTRY(disable_coprocessor) + entry sp, 16 + rsil a7, LOCKLEVEL + rsr a3, CPENABLE + movi a4, 1 + ssl a2 + sll a4, a4 + and a4, a3, a4 + xor a3, a3, a4 + wsr a3, CPENABLE + wsr a7, PS + rsync + retw + +ENTRY(enable_coprocessor) + entry sp, 16 + rsil a7, LOCKLEVEL + rsr a3, CPENABLE + movi a4, 1 + ssl a2 + sll a4, a4 + or a3, a3, a4 + wsr a3, CPENABLE + wsr a7, PS + rsync + retw + +#endif + +ENTRY(save_coprocessor_extra) + entry sp, 16 + xchal_extra_store_funcbody + retw + +ENTRY(restore_coprocessor_extra) + entry sp, 16 + xchal_extra_load_funcbody + retw + +ENTRY(save_coprocessor_registers) + entry sp, 16 + xchal_cpi_store_funcbody + retw + +ENTRY(restore_coprocessor_registers) + entry sp, 16 + xchal_cpi_load_funcbody + retw + + +/* + * The Xtensa compile-time HAL (core.h) XCHAL_*_SA_CONTENTS_LIBDB macros + * describe the contents of coprocessor & extra save areas in terms of + * undefined CONTENTS_LIBDB_{SREG,UREG,REGF} macros. We define these + * latter macros here; they expand into a table of the format we want. + * The general format is: + * + * CONTENTS_LIBDB_SREG(libdbnum, offset, size, align, rsv1, name, sregnum, + * bitmask, rsv2, rsv3) + * CONTENTS_LIBDB_UREG(libdbnum, offset, size, align, rsv1, name, uregnum, + * bitmask, rsv2, rsv3) + * CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index, + * numentries, contentsize, regname_base, + * regfile_name, rsv2, rsv3) + * + * For this table, we only care about the , and + * fields. + */ + +/* Map all XCHAL CONTENTS macros to the reg_entry asm macro defined below: */ + +#define CONTENTS_LIBDB_SREG(libdbnum,offset,size,align,rsv1,name,sregnum, \ + bitmask, rsv2, rsv3) \ + reg_entry libdbnum, offset, size ; +#define CONTENTS_LIBDB_UREG(libdbnum,offset,size,align,rsv1,name,uregnum, \ + bitmask, rsv2, rsv3) \ + reg_entry libdbnum, offset, size ; +#define CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index, \ + numentries, contentsize, regname_base, \ + regfile_name, rsv2, rsv3) \ + reg_entry libdbnum, offset, size ; + +/* A single table entry: */ + .macro reg_entry libdbnum, offset, size + .ifne (__last_offset-(__last_group_offset+\offset)) + /* padding entry */ + .word (0xFC000000+__last_offset-(__last_group_offset+\offset)) + .endif + .word \libdbnum /* actual entry */ + .set __last_offset, __last_group_offset+\offset+\size + .endm /* reg_entry */ + + +/* Table entry that marks the beginning of a group (coprocessor or "extra"): */ + .macro reg_group cpnum, num_entries, align + .set __last_group_offset, (__last_offset + \align- 1) & -\align + .ifne \num_entries + .word 0xFD000000+(\cpnum<<16)+\num_entries + .endif + .endm /* reg_group */ + +/* + * Register info tables. + */ + + .section .rodata, "a" + .globl _xtensa_reginfo_tables + .globl _xtensa_reginfo_table_size + .align 4 +_xtensa_reginfo_table_size: + .word _xtensa_reginfo_table_end - _xtensa_reginfo_tables + +_xtensa_reginfo_tables: + .set __last_offset, 0 + reg_group 0xFF, XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM, XCHAL_EXTRA_SA_ALIGN + XCHAL_EXTRA_SA_CONTENTS_LIBDB + reg_group 0, XCHAL_CP0_SA_CONTENTS_LIBDB_NUM, XCHAL_CP0_SA_ALIGN + XCHAL_CP0_SA_CONTENTS_LIBDB + reg_group 1, XCHAL_CP1_SA_CONTENTS_LIBDB_NUM, XCHAL_CP1_SA_ALIGN + XCHAL_CP1_SA_CONTENTS_LIBDB + reg_group 2, XCHAL_CP2_SA_CONTENTS_LIBDB_NUM, XCHAL_CP2_SA_ALIGN + XCHAL_CP2_SA_CONTENTS_LIBDB + reg_group 3, XCHAL_CP3_SA_CONTENTS_LIBDB_NUM, XCHAL_CP3_SA_ALIGN + XCHAL_CP3_SA_CONTENTS_LIBDB + reg_group 4, XCHAL_CP4_SA_CONTENTS_LIBDB_NUM, XCHAL_CP4_SA_ALIGN + XCHAL_CP4_SA_CONTENTS_LIBDB + reg_group 5, XCHAL_CP5_SA_CONTENTS_LIBDB_NUM, XCHAL_CP5_SA_ALIGN + XCHAL_CP5_SA_CONTENTS_LIBDB + reg_group 6, XCHAL_CP6_SA_CONTENTS_LIBDB_NUM, XCHAL_CP6_SA_ALIGN + XCHAL_CP6_SA_CONTENTS_LIBDB + reg_group 7, XCHAL_CP7_SA_CONTENTS_LIBDB_NUM, XCHAL_CP7_SA_ALIGN + XCHAL_CP7_SA_CONTENTS_LIBDB + .word 0xFC000000 /* invalid register number,marks end of table*/ +_xtensa_reginfo_table_end: + diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S new file mode 100644 index 000000000000..c64a01f71de6 --- /dev/null +++ b/arch/xtensa/kernel/entry.S @@ -0,0 +1,1996 @@ +/* + * arch/xtensa/kernel/entry.S + * + * Low-level exception handling + * + * 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) 2004-2005 by Tensilica Inc. + * + * Chris Zankel + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Unimplemented features. */ + +#undef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION +#undef KERNEL_STACK_OVERFLOW_CHECK +#undef PREEMPTIBLE_KERNEL +#undef ALLOCA_EXCEPTION_IN_IRAM + +/* Not well tested. + * + * - fast_coprocessor + */ + +/* + * Macro to find first bit set in WINDOWBASE from the left + 1 + * + * 100....0 -> 1 + * 010....0 -> 2 + * 000....1 -> WSBITS + */ + + .macro ffs_ws bit mask + +#if XCHAL_HAVE_NSA + nsau \bit, \mask # 32-WSBITS ... 31 (32 iff 0) + addi \bit, \bit, WSBITS - 32 + 1 # uppest bit set -> return 1 +#else + movi \bit, WSBITS +#if WSBITS > 16 + _bltui \mask, 0x10000, 99f + addi \bit, \bit, -16 + extui \mask, \mask, 16, 16 +#endif +#if WSBITS > 8 +99: _bltui \mask, 0x100, 99f + addi \bit, \bit, -8 + srli \mask, \mask, 8 +#endif +99: _bltui \mask, 0x10, 99f + addi \bit, \bit, -4 + srli \mask, \mask, 4 +99: _bltui \mask, 0x4, 99f + addi \bit, \bit, -2 + srli \mask, \mask, 2 +99: _bltui \mask, 0x2, 99f + addi \bit, \bit, -1 +99: + +#endif + .endm + +/* ----------------- DEFAULT FIRST LEVEL EXCEPTION HANDLERS ----------------- */ + +/* + * First-level exception handler for user exceptions. + * Save some special registers, extra states and all registers in the AR + * register file that were in use in the user task, and jump to the common + * exception code. + * We save SAR (used to calculate WMASK), and WB and WS (we don't have to + * save them for kernel exceptions). + * + * Entry condition for user_exception: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original value in depc + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + * + * Entry condition for _user_exception: + * + * a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC + * excsave has been restored, and + * stack pointer (a1) has been set. + * + * Note: _user_exception might be at an odd adress. Don't use call0..call12 + */ + +ENTRY(user_exception) + + /* Save a2, a3, and depc, restore excsave_1 and set SP. */ + + xsr a3, EXCSAVE_1 + rsr a0, DEPC + s32i a1, a2, PT_AREG1 + s32i a0, a2, PT_AREG2 + s32i a3, a2, PT_AREG3 + mov a1, a2 + + .globl _user_exception +_user_exception: + + /* Save SAR and turn off single stepping */ + + movi a2, 0 + rsr a3, SAR + wsr a2, ICOUNTLEVEL + s32i a3, a1, PT_SAR + + /* Rotate ws so that the current windowbase is at bit0. */ + /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */ + + rsr a2, WINDOWBASE + rsr a3, WINDOWSTART + ssr a2 + s32i a2, a1, PT_WINDOWBASE + s32i a3, a1, PT_WINDOWSTART + slli a2, a3, 32-WSBITS + src a2, a3, a2 + srli a2, a2, 32-WSBITS + s32i a2, a1, PT_WMASK # needed for restoring registers + + /* Save only live registers. */ + + _bbsi.l a2, 1, 1f + s32i a4, a1, PT_AREG4 + s32i a5, a1, PT_AREG5 + s32i a6, a1, PT_AREG6 + s32i a7, a1, PT_AREG7 + _bbsi.l a2, 2, 1f + s32i a8, a1, PT_AREG8 + s32i a9, a1, PT_AREG9 + s32i a10, a1, PT_AREG10 + s32i a11, a1, PT_AREG11 + _bbsi.l a2, 3, 1f + s32i a12, a1, PT_AREG12 + s32i a13, a1, PT_AREG13 + s32i a14, a1, PT_AREG14 + s32i a15, a1, PT_AREG15 + _bnei a2, 1, 1f # only one valid frame? + + /* Only one valid frame, skip saving regs. */ + + j 2f + + /* Save the remaining registers. + * We have to save all registers up to the first '1' from + * the right, except the current frame (bit 0). + * Assume a2 is: 001001000110001 + * All regiser frames starting from the top fiel to the marked '1' + * must be saved. + */ + +1: addi a3, a2, -1 # eliminate '1' in bit 0: yyyyxxww0 + neg a3, a3 # yyyyxxww0 -> YYYYXXWW1+1 + and a3, a3, a2 # max. only one bit is set + + /* Find number of frames to save */ + + ffs_ws a0, a3 # number of frames to the '1' from left + + /* Store information into WMASK: + * bits 0..3: xxx1 masked lower 4 bits of the rotated windowstart, + * bits 4...: number of valid 4-register frames + */ + + slli a3, a0, 4 # number of frames to save in bits 8..4 + extui a2, a2, 0, 4 # mask for the first 16 registers + or a2, a3, a2 + s32i a2, a1, PT_WMASK # needed when we restore the reg-file + + /* Save 4 registers at a time */ + +1: rotw -1 + s32i a0, a5, PT_AREG_END - 16 + s32i a1, a5, PT_AREG_END - 12 + s32i a2, a5, PT_AREG_END - 8 + s32i a3, a5, PT_AREG_END - 4 + addi a0, a4, -1 + addi a1, a5, -16 + _bnez a0, 1b + + /* WINDOWBASE still in SAR! */ + + rsr a2, SAR # original WINDOWBASE + movi a3, 1 + ssl a2 + sll a3, a3 + wsr a3, WINDOWSTART # set corresponding WINDOWSTART bit + wsr a2, WINDOWBASE # and WINDOWSTART + rsync + + /* We are back to the original stack pointer (a1) */ + +2: +#if XCHAL_EXTRA_SA_SIZE + + /* For user exceptions, save the extra state into the user's TCB. + * Note: We must assume that xchal_extra_store_funcbody destroys a2..a15 + */ + + GET_CURRENT(a2,a1) + addi a2, a2, THREAD_CP_SAVE + xchal_extra_store_funcbody +#endif + + /* Now, jump to the common exception handler. */ + + j common_exception + + +/* + * First-level exit handler for kernel exceptions + * Save special registers and the live window frame. + * Note: Even though we changes the stack pointer, we don't have to do a + * MOVSP here, as we do that when we return from the exception. + * (See comment in the kernel exception exit code) + * + * Entry condition for kernel_exception: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + * + * Entry condition for _kernel_exception: + * + * a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC + * excsave has been restored, and + * stack pointer (a1) has been set. + * + * Note: _kernel_exception might be at an odd adress. Don't use call0..call12 + */ + +ENTRY(kernel_exception) + + /* Save a0, a2, a3, DEPC and set SP. */ + + xsr a3, EXCSAVE_1 # restore a3, excsave_1 + rsr a0, DEPC # get a2 + s32i a1, a2, PT_AREG1 + s32i a0, a2, PT_AREG2 + s32i a3, a2, PT_AREG3 + mov a1, a2 + + .globl _kernel_exception +_kernel_exception: + + /* Save SAR and turn off single stepping */ + + movi a2, 0 + rsr a3, SAR + wsr a2, ICOUNTLEVEL + s32i a3, a1, PT_SAR + + /* Rotate ws so that the current windowbase is at bit0. */ + /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */ + + rsr a2, WINDOWBASE # don't need to save these, we only + rsr a3, WINDOWSTART # need shifted windowstart: windowmask + ssr a2 + slli a2, a3, 32-WSBITS + src a2, a3, a2 + srli a2, a2, 32-WSBITS + s32i a2, a1, PT_WMASK # needed for kernel_exception_exit + + /* Save only the live window-frame */ + + _bbsi.l a2, 1, 1f + s32i a4, a1, PT_AREG4 + s32i a5, a1, PT_AREG5 + s32i a6, a1, PT_AREG6 + s32i a7, a1, PT_AREG7 + _bbsi.l a2, 2, 1f + s32i a8, a1, PT_AREG8 + s32i a9, a1, PT_AREG9 + s32i a10, a1, PT_AREG10 + s32i a11, a1, PT_AREG11 + _bbsi.l a2, 3, 1f + s32i a12, a1, PT_AREG12 + s32i a13, a1, PT_AREG13 + s32i a14, a1, PT_AREG14 + s32i a15, a1, PT_AREG15 + +1: + +#ifdef KERNEL_STACK_OVERFLOW_CHECK + + /* Stack overflow check, for debugging */ + extui a2, a1, TASK_SIZE_BITS,XX + movi a3, SIZE?? + _bge a2, a3, out_of_stack_panic + +#endif + +/* + * This is the common exception handler. + * We get here from the user exception handler or simply by falling through + * from the kernel exception handler. + * Save the remaining special registers, switch to kernel mode, and jump + * to the second-level exception handler. + * + */ + +common_exception: + + /* Save EXCVADDR, DEBUGCAUSE, and PC, and clear LCOUNT */ + + rsr a2, DEBUGCAUSE + rsr a3, EPC_1 + s32i a2, a1, PT_DEBUGCAUSE + s32i a3, a1, PT_PC + + rsr a3, EXCVADDR + movi a2, 0 + s32i a3, a1, PT_EXCVADDR + xsr a2, LCOUNT + s32i a2, a1, PT_LCOUNT + + /* It is now save to restore the EXC_TABLE_FIXUP variable. */ + + rsr a0, EXCCAUSE + movi a3, 0 + rsr a2, EXCSAVE_1 + s32i a0, a1, PT_EXCCAUSE + s32i a3, a2, EXC_TABLE_FIXUP + + /* All unrecoverable states are saved on stack, now, and a1 is valid, + * so we can allow exceptions and interrupts (*) again. + * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X) + * + * (*) We only allow interrupts if PS.INTLEVEL was not set to 1 before + * (interrupts disabled) and if this exception is not an interrupt. + */ + + rsr a3, PS + addi a0, a0, -4 + movi a2, 1 + extui a3, a3, 0, 1 # a3 = PS.INTLEVEL[0] + moveqz a3, a2, a0 # a3 = 1 iff interrupt exception + movi a2, PS_WOE_MASK + or a3, a3, a2 + rsr a0, EXCCAUSE + xsr a3, PS + + s32i a3, a1, PT_PS # save ps + + /* Save LBEG, LEND */ + + rsr a2, LBEG + rsr a3, LEND + s32i a2, a1, PT_LBEG + s32i a3, a1, PT_LEND + + /* Go to second-level dispatcher. Set up parameters to pass to the + * exception handler and call the exception handler. + */ + + movi a4, exc_table + mov a6, a1 # pass stack frame + mov a7, a0 # pass EXCCAUSE + addx4 a4, a0, a4 + l32i a4, a4, EXC_TABLE_DEFAULT # load handler + + /* Call the second-level handler */ + + callx4 a4 + + /* Jump here for exception exit */ + +common_exception_return: + + /* Jump if we are returning from kernel exceptions. */ + +1: l32i a3, a1, PT_PS + _bbsi.l a3, PS_UM_SHIFT, 2f + j kernel_exception_exit + + /* Specific to a user exception exit: + * We need to check some flags for signal handling and rescheduling, + * and have to restore WB and WS, extra states, and all registers + * in the register file that were in use in the user task. + */ + +2: wsr a3, PS /* disable interrupts */ + + /* Check for signals (keep interrupts disabled while we read TI_FLAGS) + * Note: PS.INTLEVEL = 0, PS.EXCM = 1 + */ + + GET_THREAD_INFO(a2,a1) + l32i a4, a2, TI_FLAGS + + /* Enable interrupts again. + * Note: When we get here, we certainly have handled any interrupts. + * (Hint: There is only one user exception frame on stack) + */ + + movi a3, PS_WOE_MASK + + _bbsi.l a4, TIF_NEED_RESCHED, 3f + _bbci.l a4, TIF_SIGPENDING, 4f + +#ifndef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION + l32i a4, a1, PT_DEPC + bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f +#endif + + /* Reenable interrupts and call do_signal() */ + + wsr a3, PS + movi a4, do_signal # int do_signal(struct pt_regs*, sigset_t*) + mov a6, a1 + movi a7, 0 + callx4 a4 + j 1b + +3: /* Reenable interrupts and reschedule */ + + wsr a3, PS + movi a4, schedule # void schedule (void) + callx4 a4 + j 1b + + /* Restore the state of the task and return from the exception. */ + + + /* If we are returning from a user exception, and the process + * to run next has PT_SINGLESTEP set, we want to setup + * ICOUNT and ICOUNTLEVEL to step one instruction. + * PT_SINGLESTEP is set by sys_ptrace (ptrace.c) + */ + +4: /* a2 holds GET_CURRENT(a2,a1) */ + + l32i a3, a2, TI_TASK + l32i a3, a3, TASK_PTRACE + bbci.l a3, PT_SINGLESTEP_BIT, 1f # jump if single-step flag is not set + + movi a3, -2 # PT_SINGLESTEP flag is set, + movi a4, 1 # icountlevel of 1 means it won't + wsr a3, ICOUNT # start counting until after rfe + wsr a4, ICOUNTLEVEL # so setup icount & icountlevel. + isync + +1: + +#if XCHAL_EXTRA_SA_SIZE + + /* For user exceptions, restore the extra state from the user's TCB. */ + + /* Note: a2 still contains GET_CURRENT(a2,a1) */ + addi a2, a2, THREAD_CP_SAVE + xchal_extra_load_funcbody + + /* We must assume that xchal_extra_store_funcbody destroys + * registers a2..a15. FIXME, this list can eventually be + * reduced once real register requirements of the macro are + * finalized. */ + +#endif /* XCHAL_EXTRA_SA_SIZE */ + + + /* Switch to the user thread WINDOWBASE. Save SP temporarily in DEPC */ + + l32i a2, a1, PT_WINDOWBASE + l32i a3, a1, PT_WINDOWSTART + wsr a1, DEPC # use DEPC as temp storage + wsr a3, WINDOWSTART # restore WINDOWSTART + ssr a2 # preserve user's WB in the SAR + wsr a2, WINDOWBASE # switch to user's saved WB + rsync + rsr a1, DEPC # restore stack pointer + l32i a2, a1, PT_WMASK # register frames saved (in bits 4...9) + rotw -1 # we restore a4..a7 + _bltui a6, 16, 1f # only have to restore current window? + + /* The working registers are a0 and a3. We are restoring to + * a4..a7. Be careful not to destroy what we have just restored. + * Note: wmask has the format YYYYM: + * Y: number of registers saved in groups of 4 + * M: 4 bit mask of first 16 registers + */ + + mov a2, a6 + mov a3, a5 + +2: rotw -1 # a0..a3 become a4..a7 + addi a3, a7, -4*4 # next iteration + addi a2, a6, -16 # decrementing Y in WMASK + l32i a4, a3, PT_AREG_END + 0 + l32i a5, a3, PT_AREG_END + 4 + l32i a6, a3, PT_AREG_END + 8 + l32i a7, a3, PT_AREG_END + 12 + _bgeui a2, 16, 2b + + /* Clear unrestored registers (don't leak anything to user-land */ + +1: rsr a0, WINDOWBASE + rsr a3, SAR + sub a3, a0, a3 + beqz a3, 2f + extui a3, a3, 0, WBBITS + +1: rotw -1 + addi a3, a7, -1 + movi a4, 0 + movi a5, 0 + movi a6, 0 + movi a7, 0 + bgei a3, 1, 1b + + /* We are back were we were when we started. + * Note: a2 still contains WMASK (if we've returned to the original + * frame where we had loaded a2), or at least the lower 4 bits + * (if we have restored WSBITS-1 frames). + */ + +2: j common_exception_exit + + /* This is the kernel exception exit. + * We avoided to do a MOVSP when we entered the exception, but we + * have to do it here. + */ + +kernel_exception_exit: + + /* Disable interrupts (a3 holds PT_PS) */ + + wsr a3, PS + +#ifdef PREEMPTIBLE_KERNEL + +#ifdef CONFIG_PREEMPT + + /* + * Note: We've just returned from a call4, so we have + * at least 4 addt'l regs. + */ + + /* Check current_thread_info->preempt_count */ + + GET_THREAD_INFO(a2) + l32i a3, a2, TI_PREEMPT + bnez a3, 1f + + l32i a2, a2, TI_FLAGS + +1: + +#endif + +#endif + + /* Check if we have to do a movsp. + * + * We only have to do a movsp if the previous window-frame has + * been spilled to the *temporary* exception stack instead of the + * task's stack. This is the case if the corresponding bit in + * WINDOWSTART for the previous window-frame was set before + * (not spilled) but is zero now (spilled). + * If this bit is zero, all other bits except the one for the + * current window frame are also zero. So, we can use a simple test: + * 'and' WINDOWSTART and WINDOWSTART-1: + * + * (XXXXXX1[0]* - 1) AND XXXXXX1[0]* = XXXXXX0[0]* + * + * The result is zero only if one bit was set. + * + * (Note: We might have gone through several task switches before + * we come back to the current task, so WINDOWBASE might be + * different from the time the exception occurred.) + */ + + /* Test WINDOWSTART before and after the exception. + * We actually have WMASK, so we only have to test if it is 1 or not. + */ + + l32i a2, a1, PT_WMASK + _beqi a2, 1, common_exception_exit # Spilled before exception,jump + + /* Test WINDOWSTART now. If spilled, do the movsp */ + + rsr a3, WINDOWSTART + addi a0, a3, -1 + and a3, a3, a0 + _bnez a3, common_exception_exit + + /* Do a movsp (we returned from a call4, so we have at least a0..a7) */ + + addi a0, a1, -16 + l32i a3, a0, 0 + l32i a4, a0, 4 + s32i a3, a1, PT_SIZE+0 + s32i a4, a1, PT_SIZE+4 + l32i a3, a0, 8 + l32i a4, a0, 12 + s32i a3, a1, PT_SIZE+8 + s32i a4, a1, PT_SIZE+12 + + /* Common exception exit. + * We restore the special register and the current window frame, and + * return from the exception. + * + * Note: We expect a2 to hold PT_WMASK + */ + +common_exception_exit: + + _bbsi.l a2, 1, 1f + l32i a4, a1, PT_AREG4 + l32i a5, a1, PT_AREG5 + l32i a6, a1, PT_AREG6 + l32i a7, a1, PT_AREG7 + _bbsi.l a2, 2, 1f + l32i a8, a1, PT_AREG8 + l32i a9, a1, PT_AREG9 + l32i a10, a1, PT_AREG10 + l32i a11, a1, PT_AREG11 + _bbsi.l a2, 3, 1f + l32i a12, a1, PT_AREG12 + l32i a13, a1, PT_AREG13 + l32i a14, a1, PT_AREG14 + l32i a15, a1, PT_AREG15 + + /* Restore PC, SAR */ + +1: l32i a2, a1, PT_PC + l32i a3, a1, PT_SAR + wsr a2, EPC_1 + wsr a3, SAR + + /* Restore LBEG, LEND, LCOUNT */ + + l32i a2, a1, PT_LBEG + l32i a3, a1, PT_LEND + wsr a2, LBEG + l32i a2, a1, PT_LCOUNT + wsr a3, LEND + wsr a2, LCOUNT + + /* Check if it was double exception. */ + + l32i a0, a1, PT_DEPC + l32i a3, a1, PT_AREG3 + l32i a2, a1, PT_AREG2 + _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f + + /* Restore a0...a3 and return */ + + l32i a0, a1, PT_AREG0 + l32i a1, a1, PT_AREG1 + rfe + +1: wsr a0, DEPC + l32i a0, a1, PT_AREG0 + l32i a1, a1, PT_AREG1 + rfde + +/* + * Debug exception handler. + * + * Currently, we don't support KGDB, so only user application can be debugged. + * + * When we get here, a0 is trashed and saved to excsave[debuglevel] + */ + +ENTRY(debug_exception) + + rsr a0, EPS + XCHAL_DEBUGLEVEL + bbsi.l a0, PS_EXCM_SHIFT, 1f # exception mode + + /* Set EPC_1 and EXCCAUSE */ + + wsr a2, DEPC # save a2 temporarily + rsr a2, EPC + XCHAL_DEBUGLEVEL + wsr a2, EPC_1 + + movi a2, EXCCAUSE_MAPPED_DEBUG + wsr a2, EXCCAUSE + + /* Restore PS to the value before the debug exc but with PS.EXCM set.*/ + + movi a2, 1 << PS_EXCM_SHIFT + or a2, a0, a2 + movi a0, debug_exception # restore a3, debug jump vector + wsr a2, PS + xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL + + /* Switch to kernel/user stack, restore jump vector, and save a0 */ + + bbsi.l a2, PS_UM_SHIFT, 2f # jump if user mode + + addi a2, a1, -16-PT_SIZE # assume kernel stack + s32i a0, a2, PT_AREG0 + movi a0, 0 + s32i a1, a2, PT_AREG1 + s32i a0, a2, PT_DEPC # mark it as a regular exception + xsr a0, DEPC + s32i a3, a2, PT_AREG3 + s32i a0, a2, PT_AREG2 + mov a1, a2 + j _kernel_exception + +2: rsr a2, EXCSAVE_1 + l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer + s32i a0, a2, PT_AREG0 + movi a0, 0 + s32i a1, a2, PT_AREG1 + s32i a0, a2, PT_DEPC + xsr a0, DEPC + s32i a3, a2, PT_AREG3 + s32i a0, a2, PT_AREG2 + mov a1, a2 + j _user_exception + + /* Debug exception while in exception mode. */ +1: j 1b // FIXME!! + + +/* + * We get here in case of an unrecoverable exception. + * The only thing we can do is to be nice and print a panic message. + * We only produce a single stack frame for panic, so ??? + * + * + * Entry conditions: + * + * - a0 contains the caller address; original value saved in excsave1. + * - the original a0 contains a valid return address (backtrace) or 0. + * - a2 contains a valid stackpointer + * + * Notes: + * + * - If the stack pointer could be invalid, the caller has to setup a + * dummy stack pointer (e.g. the stack of the init_task) + * + * - If the return address could be invalid, the caller has to set it + * to 0, so the backtrace would stop. + * + */ + .align 4 +unrecoverable_text: + .ascii "Unrecoverable error in exception handler\0" + +ENTRY(unrecoverable_exception) + + movi a0, 1 + movi a1, 0 + + wsr a0, WINDOWSTART + wsr a1, WINDOWBASE + rsync + + movi a1, PS_WOE_MASK | 1 + wsr a1, PS + rsync + + movi a1, init_task + movi a0, 0 + addi a1, a1, PT_REGS_OFFSET + + movi a4, panic + movi a6, unrecoverable_text + + callx4 a4 + +1: j 1b + + +/* -------------------------- FAST EXCEPTION HANDLERS ----------------------- */ + +/* + * Fast-handler for alloca exceptions + * + * The ALLOCA handler is entered when user code executes the MOVSP + * instruction and the caller's frame is not in the register file. + * In this case, the caller frame's a0..a3 are on the stack just + * below sp (a1), and this handler moves them. + * + * For "MOVSP ," without destination register a1, this routine + * simply moves the value from to without moving the save area. + * + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + */ + +#if XCHAL_HAVE_BE +#define _EXTUI_MOVSP_SRC(ar) extui ar, ar, 4, 4 +#define _EXTUI_MOVSP_DST(ar) extui ar, ar, 0, 4 +#else +#define _EXTUI_MOVSP_SRC(ar) extui ar, ar, 0, 4 +#define _EXTUI_MOVSP_DST(ar) extui ar, ar, 4, 4 +#endif + +ENTRY(fast_alloca) + + /* We shouldn't be in a double exception. */ + + l32i a0, a2, PT_DEPC + _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, .Lunhandled_double + + rsr a0, DEPC # get a2 + s32i a4, a2, PT_AREG4 # save a4 and + s32i a0, a2, PT_AREG2 # a2 to stack + + /* Exit critical section. */ + + movi a0, 0 + s32i a0, a3, EXC_TABLE_FIXUP + + /* Restore a3, excsave_1 */ + + xsr a3, EXCSAVE_1 # make sure excsave_1 is valid for dbl. + rsr a4, EPC_1 # get exception address + s32i a3, a2, PT_AREG3 # save a3 to stack + +#ifdef ALLOCA_EXCEPTION_IN_IRAM +#error iram not supported +#else + /* Note: l8ui not allowed in IRAM/IROM!! */ + l8ui a0, a4, 1 # read as(src) from MOVSP instruction +#endif + movi a3, .Lmovsp_src + _EXTUI_MOVSP_SRC(a0) # extract source register number + addx8 a3, a0, a3 + jx a3 + +.Lunhandled_double: + wsr a0, EXCSAVE_1 + movi a0, unrecoverable_exception + callx0 a0 + + .align 8 +.Lmovsp_src: + l32i a3, a2, PT_AREG0; _j 1f; .align 8 + mov a3, a1; _j 1f; .align 8 + l32i a3, a2, PT_AREG2; _j 1f; .align 8 + l32i a3, a2, PT_AREG3; _j 1f; .align 8 + l32i a3, a2, PT_AREG4; _j 1f; .align 8 + mov a3, a5; _j 1f; .align 8 + mov a3, a6; _j 1f; .align 8 + mov a3, a7; _j 1f; .align 8 + mov a3, a8; _j 1f; .align 8 + mov a3, a9; _j 1f; .align 8 + mov a3, a10; _j 1f; .align 8 + mov a3, a11; _j 1f; .align 8 + mov a3, a12; _j 1f; .align 8 + mov a3, a13; _j 1f; .align 8 + mov a3, a14; _j 1f; .align 8 + mov a3, a15; _j 1f; .align 8 + +1: + +#ifdef ALLOCA_EXCEPTION_IN_IRAM +#error iram not supported +#else + l8ui a0, a4, 0 # read ar(dst) from MOVSP instruction +#endif + addi a4, a4, 3 # step over movsp + _EXTUI_MOVSP_DST(a0) # extract destination register + wsr a4, EPC_1 # save new epc_1 + + _bnei a0, 1, 1f # no 'movsp a1, ax': jump + + /* Move the save area. This implies the use of the L32E + * and S32E instructions, because this move must be done with + * the user's PS.RING privilege levels, not with ring 0 + * (kernel's) privileges currently active with PS.EXCM + * set. Note that we have stil registered a fixup routine with the + * double exception vector in case a double exception occurs. + */ + + /* a0,a4:avail a1:old user stack a2:exc. stack a3:new user stack. */ + + l32e a0, a1, -16 + l32e a4, a1, -12 + s32e a0, a3, -16 + s32e a4, a3, -12 + l32e a0, a1, -8 + l32e a4, a1, -4 + s32e a0, a3, -8 + s32e a4, a3, -4 + + /* Restore stack-pointer and all the other saved registers. */ + + mov a1, a3 + + l32i a4, a2, PT_AREG4 + l32i a3, a2, PT_AREG3 + l32i a0, a2, PT_AREG0 + l32i a2, a2, PT_AREG2 + rfe + + /* MOVSP , was invoked with != a1. + * Because the stack pointer is not being modified, + * we should be able to just modify the pointer + * without moving any save area. + * The processor only traps these occurrences if the + * caller window isn't live, so unfortunately we can't + * use this as an alternate trap mechanism. + * So we just do the move. This requires that we + * resolve the destination register, not just the source, + * so there's some extra work. + * (PERHAPS NOT REALLY NEEDED, BUT CLEANER...) + */ + + /* a0 dst-reg, a1 user-stack, a2 stack, a3 value of src reg. */ + +1: movi a4, .Lmovsp_dst + addx8 a4, a0, a4 + jx a4 + + .align 8 +.Lmovsp_dst: + s32i a3, a2, PT_AREG0; _j 1f; .align 8 + mov a1, a3; _j 1f; .align 8 + s32i a3, a2, PT_AREG2; _j 1f; .align 8 + s32i a3, a2, PT_AREG3; _j 1f; .align 8 + s32i a3, a2, PT_AREG4; _j 1f; .align 8 + mov a5, a3; _j 1f; .align 8 + mov a6, a3; _j 1f; .align 8 + mov a7, a3; _j 1f; .align 8 + mov a8, a3; _j 1f; .align 8 + mov a9, a3; _j 1f; .align 8 + mov a10, a3; _j 1f; .align 8 + mov a11, a3; _j 1f; .align 8 + mov a12, a3; _j 1f; .align 8 + mov a13, a3; _j 1f; .align 8 + mov a14, a3; _j 1f; .align 8 + mov a15, a3; _j 1f; .align 8 + +1: l32i a4, a2, PT_AREG4 + l32i a3, a2, PT_AREG3 + l32i a0, a2, PT_AREG0 + l32i a2, a2, PT_AREG2 + rfe + + +/* + * fast system calls. + * + * WARNING: The kernel doesn't save the entire user context before + * handling a fast system call. These functions are small and short, + * usually offering some functionality not available to user tasks. + * + * BE CAREFUL TO PRESERVE THE USER'S CONTEXT. + * + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + */ + +ENTRY(fast_syscall_kernel) + + /* Skip syscall. */ + + rsr a0, EPC_1 + addi a0, a0, 3 + wsr a0, EPC_1 + + l32i a0, a2, PT_DEPC + bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_syscall_unrecoverable + + rsr a0, DEPC # get syscall-nr + _beqz a0, fast_syscall_spill_registers + + addi a0, a0, -__NR_sysxtensa + _beqz a0, fast_syscall_sysxtensa + + j kernel_exception + + +ENTRY(fast_syscall_user) + + /* Skip syscall. */ + + rsr a0, EPC_1 + addi a0, a0, 3 + wsr a0, EPC_1 + + l32i a0, a2, PT_DEPC + bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_syscall_unrecoverable + + rsr a0, DEPC # get syscall-nr + _beqz a0, fast_syscall_spill_registers + + addi a0, a0, -__NR_sysxtensa + _beqz a0, fast_syscall_sysxtensa + + j user_exception + +ENTRY(fast_syscall_unrecoverable) + + /* Restore all states. */ + + l32i a0, a2, PT_AREG0 # restore a0 + xsr a2, DEPC # restore a2, depc + rsr a3, EXCSAVE_1 + + wsr a0, EXCSAVE_1 + movi a0, unrecoverable_exception + callx0 a0 + + + +/* + * sysxtensa syscall handler + * + * int sysxtensa (XTENSA_ATOMIC_SET, ptr, val, unused); + * int sysxtensa (XTENSA_ATOMIC_ADD, ptr, val, unused); + * int sysxtensa (XTENSA_ATOMIC_EXG_ADD, ptr, val, unused); + * int sysxtensa (XTENSA_ATOMIC_CMP_SWP, ptr, oldval, newval); + * a2 a6 a3 a4 a5 + * + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + * + * Note: we don't have to save a2; a2 holds the return value + * + * We use the two macros TRY and CATCH: + * + * TRY adds an entry to the __ex_table fixup table for the immediately + * following instruction. + * + * CATCH catches any exception that occurred at one of the preceeding TRY + * statements and continues from there + * + * Usage TRY l32i a0, a1, 0 + * + * done: rfe + * CATCH + * j done + */ + +#define TRY \ + .section __ex_table, "a"; \ + .word 66f, 67f; \ + .text; \ +66: + +#define CATCH \ +67: + +ENTRY(fast_syscall_sysxtensa) + + _beqz a6, 1f + _blti a6, SYSXTENSA_COUNT, 2f + +1: j user_exception + +2: xsr a3, EXCSAVE_1 # restore a3, excsave1 + s32i a7, a2, PT_AREG7 + + movi a7, 4 # sizeof(unsigned int) + verify_area a3, a7, a0, a2, .Leac + + _beqi a6, SYSXTENSA_ATOMIC_SET, .Lset + _beqi a6, SYSXTENSA_ATOMIC_EXG_ADD, .Lexg + _beqi a6, SYSXTENSA_ATOMIC_ADD, .Ladd + + /* Fall through for SYSXTENSA_ATOMIC_CMP_SWP */ + +.Lswp: /* Atomic compare and swap */ + +TRY l32i a7, a3, 0 # read old value + bne a7, a4, 1f # same as old value? jump + s32i a5, a3, 0 # different, modify value + movi a7, 1 # and return 1 + j .Lret + +1: movi a7, 0 # same values: return 0 + j .Lret + +.Ladd: /* Atomic add */ +.Lexg: /* Atomic (exchange) add */ + +TRY l32i a7, a3, 0 + add a4, a4, a7 + s32i a4, a3, 0 + j .Lret + +.Lset: /* Atomic set */ + +TRY l32i a7, a3, 0 # read old value as return value + s32i a4, a3, 0 # write new value + +.Lret: mov a0, a2 + mov a2, a7 + l32i a7, a0, PT_AREG7 + l32i a3, a0, PT_AREG3 + l32i a0, a0, PT_AREG0 + rfe + +CATCH +.Leac: movi a7, -EFAULT + j .Lret + + + +/* fast_syscall_spill_registers. + * + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * Note: We assume the stack pointer is EXC_TABLE_KSTK in the fixup handler. + * Note: We don't need to save a2 in depc (return value) + */ + +ENTRY(fast_syscall_spill_registers) + + /* Register a FIXUP handler (pass current wb as a parameter) */ + + movi a0, fast_syscall_spill_registers_fixup + s32i a0, a3, EXC_TABLE_FIXUP + rsr a0, WINDOWBASE + s32i a0, a3, EXC_TABLE_PARAM + + /* Save a3 and SAR on stack. */ + + rsr a0, SAR + xsr a3, EXCSAVE_1 # restore a3 and excsave_1 + s32i a0, a2, PT_AREG4 # store SAR to PT_AREG4 + s32i a3, a2, PT_AREG3 + + /* The spill routine might clobber a7, a11, and a15. */ + + s32i a7, a2, PT_AREG5 + s32i a11, a2, PT_AREG6 + s32i a15, a2, PT_AREG7 + + call0 _spill_registers # destroys a3, DEPC, and SAR + + /* Advance PC, restore registers and SAR, and return from exception. */ + + l32i a3, a2, PT_AREG4 + l32i a0, a2, PT_AREG0 + wsr a3, SAR + l32i a3, a2, PT_AREG3 + + /* Restore clobbered registers. */ + + l32i a7, a2, PT_AREG5 + l32i a11, a2, PT_AREG6 + l32i a15, a2, PT_AREG7 + + movi a2, 0 + rfe + +/* Fixup handler. + * + * We get here if the spill routine causes an exception, e.g. tlb miss. + * We basically restore WINDOWBASE and WINDOWSTART to the condition when + * we entered the spill routine and jump to the user exception handler. + * + * a0: value of depc, original value in depc + * a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE + * a3: exctable, original value in excsave1 + */ + +fast_syscall_spill_registers_fixup: + + rsr a2, WINDOWBASE # get current windowbase (a2 is saved) + xsr a0, DEPC # restore depc and a0 + ssl a2 # set shift (32 - WB) + + /* We need to make sure the current registers (a0-a3) are preserved. + * To do this, we simply set the bit for the current window frame + * in WS, so that the exception handlers save them to the task stack. + */ + + rsr a3, EXCSAVE_1 # get spill-mask + slli a2, a3, 1 # shift left by one + + slli a3, a2, 32-WSBITS + src a2, a2, a3 # a1 = xxwww1yyxxxwww1yy...... + wsr a2, WINDOWSTART # set corrected windowstart + + movi a3, exc_table + l32i a2, a3, EXC_TABLE_DOUBLE_SAVE # restore a2 + l32i a3, a3, EXC_TABLE_PARAM # original WB (in user task) + + /* Return to the original (user task) WINDOWBASE. + * We leave the following frame behind: + * a0, a1, a2 same + * a3: trashed (saved in excsave_1) + * depc: depc (we have to return to that address) + * excsave_1: a3 + */ + + wsr a3, WINDOWBASE + rsync + + /* We are now in the original frame when we entered _spill_registers: + * a0: return address + * a1: used, stack pointer + * a2: kernel stack pointer + * a3: available, saved in EXCSAVE_1 + * depc: exception address + * excsave: a3 + * Note: This frame might be the same as above. + */ + +#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION + /* Restore registers we precautiously saved. + * We have the value of the 'right' a3 + */ + + l32i a7, a2, PT_AREG5 + l32i a11, a2, PT_AREG6 + l32i a15, a2, PT_AREG7 +#endif + + /* Setup stack pointer. */ + + addi a2, a2, -PT_USER_SIZE + s32i a0, a2, PT_AREG0 + + /* Make sure we return to this fixup handler. */ + + movi a3, fast_syscall_spill_registers_fixup_return + s32i a3, a2, PT_DEPC # setup depc + + /* Jump to the exception handler. */ + + movi a3, exc_table + rsr a0, EXCCAUSE + addx4 a0, a0, a3 # find entry in table + l32i a0, a0, EXC_TABLE_FAST_USER # load handler + jx a0 + +fast_syscall_spill_registers_fixup_return: + + /* When we return here, all registers have been restored (a2: DEPC) */ + + wsr a2, DEPC # exception address + + /* Restore fixup handler. */ + + xsr a3, EXCSAVE_1 + movi a2, fast_syscall_spill_registers_fixup + s32i a2, a3, EXC_TABLE_FIXUP + rsr a2, WINDOWBASE + s32i a2, a3, EXC_TABLE_PARAM + l32i a2, a3, EXC_TABLE_KSTK + +#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION + /* Save registers again that might be clobbered. */ + + s32i a7, a2, PT_AREG5 + s32i a11, a2, PT_AREG6 + s32i a15, a2, PT_AREG7 +#endif + + /* Load WB at the time the exception occurred. */ + + rsr a3, SAR # WB is still in SAR + neg a3, a3 + wsr a3, WINDOWBASE + rsync + + /* Restore a3 and return. */ + + movi a3, exc_table + xsr a3, EXCSAVE_1 + + rfde + + +/* + * spill all registers. + * + * This is not a real function. The following conditions must be met: + * + * - must be called with call0. + * - uses DEPC, a3 and SAR. + * - the last 'valid' register of each frame are clobbered. + * - the caller must have registered a fixup handler + * (or be inside a critical section) + * - PS_EXCM must be set (PS_WOE cleared?) + */ + +ENTRY(_spill_registers) + + /* + * Rotate ws so that the current windowbase is at bit 0. + * Assume ws = xxxwww1yy (www1 current window frame). + * Rotate ws right so that a2 = yyxxxwww1. + */ + + wsr a2, DEPC # preserve a2 + rsr a2, WINDOWBASE + rsr a3, WINDOWSTART + ssr a2 # holds WB + slli a2, a3, WSBITS + or a3, a3, a2 # a2 = xxxwww1yyxxxwww1yy + srl a3, a3 + + /* We are done if there are no more than the current register frame. */ + + extui a3, a3, 1, WSBITS-2 # a3 = 0yyxxxwww + movi a2, (1 << (WSBITS-1)) + _beqz a3, .Lnospill # only one active frame? jump + + /* We want 1 at the top, so that we return to the current windowbase */ + + or a3, a3, a2 # 1yyxxxwww + + /* Skip empty frames - get 'oldest' WINDOWSTART-bit. */ + + wsr a3, WINDOWSTART # save shifted windowstart + neg a2, a3 + and a3, a2, a3 # first bit set from right: 000010000 + + ffs_ws a2, a3 # a2: shifts to skip empty frames + movi a3, WSBITS + sub a2, a3, a2 # WSBITS-a2:number of 0-bits from right + ssr a2 # save in SAR for later. + + rsr a3, WINDOWBASE + add a3, a3, a2 + rsr a2, DEPC # restore a2 + wsr a3, WINDOWBASE + rsync + + rsr a3, WINDOWSTART + srl a3, a3 # shift windowstart + + /* WB is now just one frame below the oldest frame in the register + window. WS is shifted so the oldest frame is in bit 0, thus, WB + and WS differ by one 4-register frame. */ + + /* Save frames. Depending what call was used (call4, call8, call12), + * we have to save 4,8. or 12 registers. + */ + + _bbsi.l a3, 1, .Lc4 + _bbsi.l a3, 2, .Lc8 + + /* Special case: we have a call12-frame starting at a4. */ + + _bbci.l a3, 3, .Lc12 # bit 3 shouldn't be zero! (Jump to Lc12 first) + + s32e a4, a1, -16 # a1 is valid with an empty spill area + l32e a4, a5, -12 + s32e a8, a4, -48 + mov a8, a4 + l32e a4, a1, -16 + j .Lc12c + +.Lloop: _bbsi.l a3, 1, .Lc4 + _bbci.l a3, 2, .Lc12 + +.Lc8: s32e a4, a13, -16 + l32e a4, a5, -12 + s32e a8, a4, -32 + s32e a5, a13, -12 + s32e a6, a13, -8 + s32e a7, a13, -4 + s32e a9, a4, -28 + s32e a10, a4, -24 + s32e a11, a4, -20 + + srli a11, a3, 2 # shift windowbase by 2 + rotw 2 + _bnei a3, 1, .Lloop + +.Lexit: /* Done. Do the final rotation, set WS, and return. */ + + rotw 1 + rsr a3, WINDOWBASE + ssl a3 + movi a3, 1 + sll a3, a3 + wsr a3, WINDOWSTART + +.Lnospill: + jx a0 + +.Lc4: s32e a4, a9, -16 + s32e a5, a9, -12 + s32e a6, a9, -8 + s32e a7, a9, -4 + + srli a7, a3, 1 + rotw 1 + _bnei a3, 1, .Lloop + j .Lexit + +.Lc12: _bbci.l a3, 3, .Linvalid_mask # bit 2 shouldn't be zero! + + /* 12-register frame (call12) */ + + l32e a2, a5, -12 + s32e a8, a2, -48 + mov a8, a2 + +.Lc12c: s32e a9, a8, -44 + s32e a10, a8, -40 + s32e a11, a8, -36 + s32e a12, a8, -32 + s32e a13, a8, -28 + s32e a14, a8, -24 + s32e a15, a8, -20 + srli a15, a3, 3 + + /* The stack pointer for a4..a7 is out of reach, so we rotate the + * window, grab the stackpointer, and rotate back. + * Alternatively, we could also use the following approach, but that + * makes the fixup routine much more complicated: + * rotw 1 + * s32e a0, a13, -16 + * ... + * rotw 2 + */ + + rotw 1 + mov a5, a13 + rotw -1 + + s32e a4, a9, -16 + s32e a5, a9, -12 + s32e a6, a9, -8 + s32e a7, a9, -4 + + rotw 3 + + _beqi a3, 1, .Lexit + j .Lloop + +.Linvalid_mask: + + /* We get here because of an unrecoverable error in the window + * registers. If we are in user space, we kill the application, + * however, this condition is unrecoverable in kernel space. + */ + + rsr a0, PS + _bbci.l a0, PS_UM_SHIFT, 1f + + /* User space: Setup a dummy frame and kill application. + * Note: We assume EXC_TABLE_KSTK contains a valid stack pointer. + */ + + movi a0, 1 + movi a1, 0 + + wsr a0, WINDOWSTART + wsr a1, WINDOWBASE + rsync + + movi a0, 0 + + movi a3, exc_table + l32i a1, a3, EXC_TABLE_KSTK + wsr a3, EXCSAVE_1 + + movi a4, PS_WOE_MASK | 1 + wsr a4, PS + rsync + + movi a6, SIGSEGV + movi a4, do_exit + callx4 a4 + +1: /* Kernel space: PANIC! */ + + wsr a0, EXCSAVE_1 + movi a0, unrecoverable_exception + callx0 a0 # should not return +1: j 1b + +/* + * We should never get here. Bail out! + */ + +ENTRY(fast_second_level_miss_double_kernel) + +1: movi a0, unrecoverable_exception + callx0 a0 # should not return +1: j 1b + +/* First-level entry handler for user, kernel, and double 2nd-level + * TLB miss exceptions. Note that for now, user and kernel miss + * exceptions share the same entry point and are handled identically. + * + * An old, less-efficient C version of this function used to exist. + * We include it below, interleaved as comments, for reference. + * + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + */ + +ENTRY(fast_second_level_miss) + + /* Save a1. Note: we don't expect a double exception. */ + + s32i a1, a2, PT_AREG1 + + /* We need to map the page of PTEs for the user task. Find + * the pointer to that page. Also, it's possible for tsk->mm + * to be NULL while tsk->active_mm is nonzero if we faulted on + * a vmalloc address. In that rare case, we must use + * active_mm instead to avoid a fault in this handler. See + * + * http://mail.nl.linux.org/linux-mm/2002-08/msg00258.html + * (or search Internet on "mm vs. active_mm") + * + * if (!mm) + * mm = tsk->active_mm; + * pgd = pgd_offset (mm, regs->excvaddr); + * pmd = pmd_offset (pgd, regs->excvaddr); + * pmdval = *pmd; + */ + + GET_CURRENT(a1,a2) + l32i a0, a1, TASK_MM # tsk->mm + beqz a0, 9f + +8: rsr a1, EXCVADDR # fault address + _PGD_OFFSET(a0, a1, a1) + l32i a0, a0, 0 # read pmdval + //beqi a0, _PAGE_USER, 2f + beqz a0, 2f + + /* Read ptevaddr and convert to top of page-table page. + * + * vpnval = read_ptevaddr_register() & PAGE_MASK; + * vpnval += DTLB_WAY_PGTABLE; + * pteval = mk_pte (virt_to_page(pmd_val(pmdval)), PAGE_KERNEL); + * write_dtlb_entry (pteval, vpnval); + * + * The messy computation for 'pteval' above really simplifies + * into the following: + * + * pteval = ((pmdval - PAGE_OFFSET) & PAGE_MASK) | PAGE_KERNEL + */ + + movi a1, -PAGE_OFFSET + add a0, a0, a1 # pmdval - PAGE_OFFSET + extui a1, a0, 0, PAGE_SHIFT # ... & PAGE_MASK + xor a0, a0, a1 + + + movi a1, PAGE_DIRECTORY + or a0, a0, a1 # ... | PAGE_DIRECTORY + + rsr a1, PTEVADDR + srli a1, a1, PAGE_SHIFT + slli a1, a1, PAGE_SHIFT # ptevaddr & PAGE_MASK + addi a1, a1, DTLB_WAY_PGTABLE # ... + way_number + + wdtlb a0, a1 + dsync + + /* Exit critical section. */ + + movi a0, 0 + s32i a0, a3, EXC_TABLE_FIXUP + + /* Restore the working registers, and return. */ + + l32i a0, a2, PT_AREG0 + l32i a1, a2, PT_AREG1 + l32i a2, a2, PT_DEPC + xsr a3, EXCSAVE_1 + + bgeui a2, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f + + /* Restore excsave1 and return. */ + + rsr a2, DEPC + rfe + + /* Return from double exception. */ + +1: xsr a2, DEPC + esync + rfde + +9: l32i a0, a1, TASK_ACTIVE_MM # unlikely case mm == 0 + j 8b + +2: /* Invalid PGD, default exception handling */ + + rsr a1, DEPC + xsr a3, EXCSAVE_1 + s32i a1, a2, PT_AREG2 + s32i a3, a2, PT_AREG3 + mov a1, a2 + + rsr a2, PS + bbsi.l a2, PS_UM_SHIFT, 1f + j _kernel_exception +1: j _user_exception + + +/* + * StoreProhibitedException + * + * Update the pte and invalidate the itlb mapping for this pte. + * + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + */ + +ENTRY(fast_store_prohibited) + + /* Save a1 and a4. */ + + s32i a1, a2, PT_AREG1 + s32i a4, a2, PT_AREG4 + + GET_CURRENT(a1,a2) + l32i a0, a1, TASK_MM # tsk->mm + beqz a0, 9f + +8: rsr a1, EXCVADDR # fault address + _PGD_OFFSET(a0, a1, a4) + l32i a0, a0, 0 + //beqi a0, _PAGE_USER, 2f # FIXME use _PAGE_INVALID + beqz a0, 2f + + _PTE_OFFSET(a0, a1, a4) + l32i a4, a0, 0 # read pteval + movi a1, _PAGE_VALID | _PAGE_RW + bnall a4, a1, 2f + + movi a1, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_WRENABLE + or a4, a4, a1 + rsr a1, EXCVADDR + s32i a4, a0, 0 + + /* We need to flush the cache if we have page coloring. */ +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK + dhwb a0, 0 +#endif + pdtlb a0, a1 + beqz a0, 1f + idtlb a0 // FIXME do we need this? + wdtlb a4, a0 +1: + + /* Exit critical section. */ + + movi a0, 0 + s32i a0, a3, EXC_TABLE_FIXUP + + /* Restore the working registers, and return. */ + + l32i a4, a2, PT_AREG4 + l32i a1, a2, PT_AREG1 + l32i a0, a2, PT_AREG0 + l32i a2, a2, PT_DEPC + + /* Restore excsave1 and a3. */ + + xsr a3, EXCSAVE_1 + bgeui a2, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f + + rsr a2, DEPC + rfe + + /* Double exception. Restore FIXUP handler and return. */ + +1: xsr a2, DEPC + esync + rfde + +9: l32i a0, a1, TASK_ACTIVE_MM # unlikely case mm == 0 + j 8b + +2: /* If there was a problem, handle fault in C */ + + rsr a4, DEPC # still holds a2 + xsr a3, EXCSAVE_1 + s32i a4, a2, PT_AREG2 + s32i a3, a2, PT_AREG3 + l32i a4, a2, PT_AREG4 + mov a1, a2 + + rsr a2, PS + bbsi.l a2, PS_UM_SHIFT, 1f + j _kernel_exception +1: j _user_exception + + +#if XCHAL_EXTRA_SA_SIZE + +#warning fast_coprocessor untested + +/* + * Entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original in DEPC + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + */ + +ENTRY(fast_coprocessor_double) + wsr a0, EXCSAVE_1 + movi a0, unrecoverable_exception + callx0 a0 + +ENTRY(fast_coprocessor) + + /* Fatal if we are in a double exception. */ + + l32i a0, a2, PT_DEPC + _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_coprocessor_double + + /* Save some registers a1, a3, a4, SAR */ + + xsr a3, EXCSAVE_1 + s32i a3, a2, PT_AREG3 + rsr a3, SAR + s32i a4, a2, PT_AREG4 + s32i a1, a2, PT_AREG1 + s32i a5, a1, PT_AREG5 + s32i a3, a2, PT_SAR + mov a1, a2 + + /* Currently, the HAL macros only guarantee saving a0 and a1. + * These can and will be refined in the future, but for now, + * just save the remaining registers of a2...a15. + */ + s32i a6, a1, PT_AREG6 + s32i a7, a1, PT_AREG7 + s32i a8, a1, PT_AREG8 + s32i a9, a1, PT_AREG9 + s32i a10, a1, PT_AREG10 + s32i a11, a1, PT_AREG11 + s32i a12, a1, PT_AREG12 + s32i a13, a1, PT_AREG13 + s32i a14, a1, PT_AREG14 + s32i a15, a1, PT_AREG15 + + /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */ + + rsr a0, EXCCAUSE + addi a3, a0, -XCHAL_EXCCAUSE_COPROCESSOR0_DISABLED + + /* Set corresponding CPENABLE bit */ + + movi a4, 1 + ssl a3 # SAR: 32 - coprocessor_number + rsr a5, CPENABLE + sll a4, a4 + or a4, a5, a4 + wsr a4, CPENABLE + rsync + movi a5, coprocessor_info # list of owner and offset into cp_save + addx8 a0, a4, a5 # entry for CP + + bne a4, a5, .Lload # bit wasn't set before, cp not in use + + /* Now compare the current task with the owner of the coprocessor. + * If they are the same, there is no reason to save or restore any + * coprocessor state. Having already enabled the coprocessor, + * branch ahead to return. + */ + GET_CURRENT(a5,a1) + l32i a4, a0, COPROCESSOR_INFO_OWNER # a4: current owner for this CP + beq a4, a5, .Ldone + + /* Find location to dump current coprocessor state: + * task_struct->task_cp_save_offset + coprocessor_offset[coprocessor] + * + * Note: a0 pointer to the entry in the coprocessor owner table, + * a3 coprocessor number, + * a4 current owner of coprocessor. + */ + l32i a5, a0, COPROCESSOR_INFO_OFFSET + addi a2, a4, THREAD_CP_SAVE + add a2, a2, a5 + + /* Store current coprocessor states. (a5 still has CP number) */ + + xchal_cpi_store_funcbody + + /* The macro might have destroyed a3 (coprocessor number), but + * SAR still has 32 - coprocessor_number! + */ + movi a3, 32 + rsr a4, SAR + sub a3, a3, a4 + +.Lload: /* A new task now owns the corpocessors. Save its TCB pointer into + * the coprocessor owner table. + * + * Note: a0 pointer to the entry in the coprocessor owner table, + * a3 coprocessor number. + */ + GET_CURRENT(a4,a1) + s32i a4, a0, 0 + + /* Find location from where to restore the current coprocessor state.*/ + + l32i a5, a0, COPROCESSOR_INFO_OFFSET + addi a2, a4, THREAD_CP_SAVE + add a2, a2, a4 + + xchal_cpi_load_funcbody + + /* We must assume that the xchal_cpi_store_funcbody macro destroyed + * registers a2..a15. + */ + +.Ldone: l32i a15, a1, PT_AREG15 + l32i a14, a1, PT_AREG14 + l32i a13, a1, PT_AREG13 + l32i a12, a1, PT_AREG12 + l32i a11, a1, PT_AREG11 + l32i a10, a1, PT_AREG10 + l32i a9, a1, PT_AREG9 + l32i a8, a1, PT_AREG8 + l32i a7, a1, PT_AREG7 + l32i a6, a1, PT_AREG6 + l32i a5, a1, PT_AREG5 + l32i a4, a1, PT_AREG4 + l32i a3, a1, PT_AREG3 + l32i a2, a1, PT_AREG2 + l32i a0, a1, PT_AREG0 + l32i a1, a1, PT_AREG1 + + rfe + +#endif /* XCHAL_EXTRA_SA_SIZE */ + +/* + * Task switch. + * + * struct task* _switch_to (struct task* prev, struct task* next) + * a2 a2 a3 + */ + +ENTRY(_switch_to) + + entry a1, 16 + + mov a4, a3 # preserve a3 + + s32i a0, a2, THREAD_RA # save return address + s32i a1, a2, THREAD_SP # save stack pointer + + /* Disable ints while we manipulate the stack pointer; spill regs. */ + + movi a5, PS_EXCM_MASK | LOCKLEVEL + xsr a5, PS + rsr a3, EXCSAVE_1 + rsync + s32i a3, a3, EXC_TABLE_FIXUP /* enter critical section */ + + call0 _spill_registers + + /* Set kernel stack (and leave critical section) + * Note: It's save to set it here. The stack will not be overwritten + * because the kernel stack will only be loaded again after + * we return from kernel space. + */ + + l32i a0, a4, TASK_THREAD_INFO + rsr a3, EXCSAVE_1 # exc_table + movi a1, 0 + addi a0, a0, PT_REGS_OFFSET + s32i a1, a3, EXC_TABLE_FIXUP + s32i a0, a3, EXC_TABLE_KSTK + + /* restore context of the task that 'next' addresses */ + + l32i a0, a4, THREAD_RA /* restore return address */ + l32i a1, a4, THREAD_SP /* restore stack pointer */ + + wsr a5, PS + rsync + + retw + + +ENTRY(ret_from_fork) + + /* void schedule_tail (struct task_struct *prev) + * Note: prev is still in a6 (return value from fake call4 frame) + */ + movi a4, schedule_tail + callx4 a4 + + movi a4, do_syscall_trace + callx4 a4 + + j common_exception_return + + + +/* + * Table of syscalls + */ + +.data +.align 4 +.global sys_call_table +sys_call_table: + +#define SYSCALL(call, narg) .word call +#include "syscalls.h" + +/* + * Number of arguments of each syscall + */ + +.global sys_narg_table +sys_narg_table: + +#undef SYSCALL +#define SYSCALL(call, narg) .byte narg +#include "syscalls.h" + diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S new file mode 100644 index 000000000000..6e9b5225b8f6 --- /dev/null +++ b/arch/xtensa/kernel/head.S @@ -0,0 +1,237 @@ +/* + * arch/xtensa/kernel/head.S + * + * Xtensa Processor startup code. + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * Marc Gauthier + * Joe Taylor + * Kevin Chea + */ + +#include +#include +#include +#include + +/* + * This module contains the entry code for kernel images. It performs the + * minimal setup needed to call the generic C routines. + * + * Prerequisites: + * + * - The kernel image has been loaded to the actual address where it was + * compiled to. + * - a2 contains either 0 or a pointer to a list of boot parameters. + * (see setup.c for more details) + * + */ + + .macro iterate from, to , cmd + .ifeq ((\to - \from) & ~0xfff) + \cmd \from + iterate "(\from+1)", \to, \cmd + .endif + .endm + +/* + * _start + * + * The bootloader passes a pointer to a list of boot parameters in a2. + */ + + /* The first bytes of the kernel image must be an instruction, so we + * manually allocate and define the literal constant we need for a jx + * instruction. + */ + + .section .head.text, "ax" + .globl _start +_start: _j 2f + .align 4 +1: .word _startup +2: l32r a0, 1b + jx a0 + + .text + .align 4 +_startup: + + /* Disable interrupts and exceptions. */ + + movi a0, XCHAL_PS_EXCM_MASK + wsr a0, PS + + /* Preserve the pointer to the boot parameter list in EXCSAVE_1 */ + + wsr a2, EXCSAVE_1 + + /* Start with a fresh windowbase and windowstart. */ + + movi a1, 1 + movi a0, 0 + wsr a1, WINDOWSTART + wsr a0, WINDOWBASE + rsync + + /* Set a0 to 0 for the remaining initialization. */ + + movi a0, 0 + + /* Clear debugging registers. */ + +#if XCHAL_HAVE_DEBUG + wsr a0, IBREAKENABLE + wsr a0, ICOUNT + movi a1, 15 + wsr a0, ICOUNTLEVEL + + .macro reset_dbreak num + wsr a0, DBREAKC + \num + .endm + + iterate 0, XCHAL_NUM_IBREAK-1, reset_dbreak +#endif + + /* Clear CCOUNT (not really necessary, but nice) */ + + wsr a0, CCOUNT # not really necessary, but nice + + /* Disable zero-loops. */ + +#if XCHAL_HAVE_LOOPS + wsr a0, LCOUNT +#endif + + /* Disable all timers. */ + + .macro reset_timer num + wsr a0, CCOMPARE_0 + \num + .endm + iterate 0, XCHAL_NUM_TIMERS-1, reset_timer + + /* Interrupt initialization. */ + + movi a2, XCHAL_INTTYPE_MASK_SOFTWARE | XCHAL_INTTYPE_MASK_EXTERN_EDGE + wsr a0, INTENABLE + wsr a2, INTCLEAR + + /* Disable coprocessors. */ + +#if XCHAL_CP_NUM > 0 + wsr a0, CPENABLE +#endif + + /* Set PS.INTLEVEL=1, PS.WOE=0, kernel stack, PS.EXCM=0 + * + * Note: PS.EXCM must be cleared before using any loop + * instructions; otherwise, they are silently disabled, and + * at most one iteration of the loop is executed. + */ + + movi a1, 1 + wsr a1, PS + rsync + + /* Initialize the caches. + * Does not include flushing writeback d-cache. + * a6, a7 are just working registers (clobbered). + */ + + icache_reset a2, a3 + dcache_reset a2, a3 + + /* Unpack data sections + * + * The linker script used to build the Linux kernel image + * creates a table located at __boot_reloc_table_start + * that contans the information what data needs to be unpacked. + * + * Uses a2-a7. + */ + + movi a2, __boot_reloc_table_start + movi a3, __boot_reloc_table_end + +1: beq a2, a3, 3f # no more entries? + l32i a4, a2, 0 # start destination (in RAM) + l32i a5, a2, 4 # end desination (in RAM) + l32i a6, a2, 8 # start source (in ROM) + addi a2, a2, 12 # next entry + beq a4, a5, 1b # skip, empty entry + beq a4, a6, 1b # skip, source and dest. are the same + +2: l32i a7, a6, 0 # load word + addi a6, a6, 4 + s32i a7, a4, 0 # store word + addi a4, a4, 4 + bltu a4, a5, 2b + j 1b + +3: + /* All code and initialized data segments have been copied. + * Now clear the BSS segment. + */ + + movi a2, _bss_start # start of BSS + movi a3, _bss_end # end of BSS + +1: addi a2, a2, 4 + s32i a0, a2, 0 + blt a2, a3, 1b + +#if XCHAL_DCACHE_IS_WRITEBACK + + /* After unpacking, flush the writeback cache to memory so the + * instructions/data are available. + */ + + dcache_writeback_all a2, a3 +#endif + + /* Setup stack and enable window exceptions (keep irqs disabled) */ + + movi a1, init_thread_union + addi a1, a1, KERNEL_STACK_SIZE + + movi a2, 0x00040001 # WOE=1, INTLEVEL=1, UM=0 + wsr a2, PS # (enable reg-windows; progmode stack) + rsync + + /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/ + + movi a2, debug_exception + wsr a2, EXCSAVE + XCHAL_DEBUGLEVEL + + /* Set up EXCSAVE[1] to point to the exc_table. */ + + movi a6, exc_table + xsr a6, EXCSAVE_1 + + /* init_arch kick-starts the linux kernel */ + + movi a4, init_arch + callx4 a4 + + movi a4, start_kernel + callx4 a4 + +should_never_return: + j should_never_return + + /* Define some common data structures here. We define them + * here in this assembly file due to their unusual alignment + * requirements. + */ + + .comm swapper_pg_dir,PAGE_SIZE,PAGE_SIZE + .comm empty_bad_page_table,PAGE_SIZE,PAGE_SIZE + .comm empty_bad_page,PAGE_SIZE,PAGE_SIZE + .comm empty_zero_page,PAGE_SIZE,PAGE_SIZE + diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c new file mode 100644 index 000000000000..4cbf6d91571f --- /dev/null +++ b/arch/xtensa/kernel/irq.c @@ -0,0 +1,192 @@ +/* + * linux/arch/xtensa/kernel/irq.c + * + * Xtensa built-in interrupt controller and some generic functions copied + * from i386. + * + * Copyright (C) 2002 - 2005 Tensilica, Inc. + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * + * Chris Zankel + * Kevin Chea + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +static void enable_xtensa_irq(unsigned int irq); +static void disable_xtensa_irq(unsigned int irq); +static void mask_and_ack_xtensa(unsigned int irq); +static void end_xtensa_irq(unsigned int irq); + +static unsigned int cached_irq_mask; + +atomic_t irq_err_count; + +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves. + */ +void ack_bad_irq(unsigned int irq) +{ + printk("unexpected IRQ trap at vector %02x\n", irq); +} + +/* + * 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, struct pt_regs *regs) +{ + irq_enter(); + +#ifdef CONFIG_DEBUG_STACKOVERFLOW + /* Debugging check for stack overflow: is there less than 1KB free? */ + { + unsigned long sp; + + __asm__ __volatile__ ("mov %0, a1\n" : "=a" (sp)); + sp &= THREAD_SIZE - 1; + + if (unlikely(sp < (sizeof(thread_info) + 1024))) + printk("Stack overflow in do_IRQ: %ld\n", + sp - sizeof(struct thread_info)); + } +#endif + + __do_IRQ(irq, regs); + + irq_exit(); + + return 1; +} + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j; + struct irqaction * action; + unsigned long flags; + + if (i == 0) { + seq_printf(p, " "); + for (j=0; jtypename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { + seq_printf(p, "NMI: "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", nmi_count(j)); + seq_putc(p, '\n'); + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + } + return 0; +} +/* shutdown is same as "disable" */ +#define shutdown_xtensa_irq disable_xtensa_irq + +static unsigned int startup_xtensa_irq(unsigned int irq) +{ + enable_xtensa_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type xtensa_irq_type = { + "Xtensa-IRQ", + startup_xtensa_irq, + shutdown_xtensa_irq, + enable_xtensa_irq, + disable_xtensa_irq, + mask_and_ack_xtensa, + end_xtensa_irq +}; + +static inline void mask_irq(unsigned int irq) +{ + cached_irq_mask &= ~(1 << irq); + set_sr (cached_irq_mask, INTENABLE); +} + +static inline void unmask_irq(unsigned int irq) +{ + cached_irq_mask |= 1 << irq; + set_sr (cached_irq_mask, INTENABLE); +} + +static void disable_xtensa_irq(unsigned int irq) +{ + unsigned long flags; + local_save_flags(flags); + mask_irq(irq); + local_irq_restore(flags); +} + +static void enable_xtensa_irq(unsigned int irq) +{ + unsigned long flags; + local_save_flags(flags); + unmask_irq(irq); + local_irq_restore(flags); +} + +static void mask_and_ack_xtensa(unsigned int irq) +{ + disable_xtensa_irq(irq); +} + +static void end_xtensa_irq(unsigned int irq) +{ + enable_xtensa_irq(irq); +} + + +void __init init_IRQ(void) +{ + int i; + + for (i=0; i < XTENSA_NR_IRQS; i++) + irq_desc[i].handler = &xtensa_irq_type; + + cached_irq_mask = 0; + + platform_init_irq(); +} diff --git a/arch/xtensa/kernel/module.c b/arch/xtensa/kernel/module.c new file mode 100644 index 000000000000..d1683cfa19a2 --- /dev/null +++ b/arch/xtensa/kernel/module.c @@ -0,0 +1,78 @@ +/* + * arch/xtensa/kernel/platform.c + * + * Module support. + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +LIST_HEAD(module_buf_list); + +void *module_alloc(unsigned long size) +{ + panic("module_alloc not implemented"); +} + +void module_free(struct module *mod, void *module_region) +{ + panic("module_free not implemented"); +} + +int module_frob_arch_sections(Elf32_Ehdr *hdr, + Elf32_Shdr *sechdrs, + char *secstrings, + struct module *me) +{ + panic("module_frob_arch_sections not implemented"); +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + panic ("apply_relocate not implemented"); +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + panic("apply_relocate_add not implemented"); +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + panic ("module_finalize not implemented"); +} + +void module_arch_cleanup(struct module *mod) +{ + panic("module_arch_cleanup not implemented"); +} + +struct bug_entry *module_find_bug(unsigned long bugaddr) +{ + panic("module_find_bug not implemented"); +} diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c new file mode 100644 index 000000000000..84fde258cf85 --- /dev/null +++ b/arch/xtensa/kernel/pci-dma.c @@ -0,0 +1,73 @@ +/* + * arch/xtensa/pci-dma.c + * + * DMA coherent memory allocation. + * + * 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. + * + * Copyright (C) 2002 - 2005 Tensilica Inc. + * + * Based on version for i386. + * + * Chris Zankel + * Joe Taylor + */ + +#include +#include +#include +#include +#include +#include + +/* + * Note: We assume that the full memory space is always mapped to 'kseg' + * Otherwise we have to use page attributes (not implemented). + */ + +void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp) +{ + void *ret; + + /* ignore region speicifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || (*dev->dma_mask < 0xffffffff)) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *handle = virt_to_bus(ret); + } + return (void*) BYPASS_ADDR((unsigned long)ret); +} + +void dma_free_coherent(struct device *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages(CACHED_ADDR((unsigned long)vaddr), get_order(size)); +} + + +void consistent_sync(void *vaddr, size_t size, int direction) +{ + switch (direction) { + case PCI_DMA_NONE: + BUG(); + case PCI_DMA_FROMDEVICE: /* invalidate only */ + __invalidate_dcache_range((unsigned long)vaddr, + (unsigned long)size); + break; + + case PCI_DMA_TODEVICE: /* writeback only */ + case PCI_DMA_BIDIRECTIONAL: /* writeback and invalidate */ + __flush_invalidate_dcache_range((unsigned long)vaddr, + (unsigned long)size); + break; + } +} diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c new file mode 100644 index 000000000000..d29a81648637 --- /dev/null +++ b/arch/xtensa/kernel/pci.c @@ -0,0 +1,563 @@ +/* + * arch/xtensa/pcibios.c + * + * PCI bios-type initialisation for PCI machines + * + * 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. + * + * Copyright (C) 2001-2005 Tensilica Inc. + * + * Based largely on work from Cort (ppc/kernel/pci.c) + * IO functions copied from sparc. + * + * Chris Zankel + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* PCI Controller */ + + +/* + * pcibios_alloc_controller + * pcibios_enable_device + * pcibios_fixups + * pcibios_align_resource + * pcibios_fixup_bus + * pcibios_setup + * pci_bus_add_device + * pci_mmap_page_range + */ + +struct pci_controller* pci_ctrl_head; +struct pci_controller** pci_ctrl_tail = &pci_ctrl_head; + +static int pci_bus_count; + +static void pcibios_fixup_resources(struct pci_dev* dev); + +#if 0 // FIXME +struct pci_fixup pcibios_fixups[] = { + { DECLARE_PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, + { 0 } +}; +#endif + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check, mask; + int reg; + struct pci_controller* pci_ctrl = dev->sysdata; + + new = res->start; + if (pci_ctrl && res->flags & IORESOURCE_IO) { + new -= pci_ctrl->io_space.base; + } + 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); + mask = (new & PCI_BASE_ADDRESS_SPACE_IO) ? + PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK; + + if ((new ^ check) & 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, + unsigned long align) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (size > 0x100) { + printk(KERN_ERR "PCI: I/O Region %s/%d too large" + " (%ld bytes)\n", dev->slot_name, + dev->resource - res, size); + } + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + +int +pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + 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; +} + +struct pci_controller * __init pcibios_alloc_controller(void) +{ + struct pci_controller *pci_ctrl; + + pci_ctrl = (struct pci_controller *)alloc_bootmem(sizeof(*pci_ctrl)); + memset(pci_ctrl, 0, sizeof(struct pci_controller)); + + *pci_ctrl_tail = pci_ctrl; + pci_ctrl_tail = &pci_ctrl->next; + + return pci_ctrl; +} + +static int __init pcibios_init(void) +{ + struct pci_controller *pci_ctrl; + struct pci_bus *bus; + int next_busno = 0, i; + + printk("PCI: Probing PCI hardware\n"); + + /* Scan all of the recorded PCI controllers. */ + for (pci_ctrl = pci_ctrl_head; pci_ctrl; pci_ctrl = pci_ctrl->next) { + pci_ctrl->last_busno = 0xff; + bus = pci_scan_bus(pci_ctrl->first_busno, pci_ctrl->ops, + pci_ctrl); + if (pci_ctrl->io_resource.flags) { + unsigned long offs; + + offs = (unsigned long)pci_ctrl->io_space.base; + pci_ctrl->io_resource.start += offs; + pci_ctrl->io_resource.end += offs; + bus->resource[0] = &pci_ctrl->io_resource; + } + for (i = 0; i < 3; ++i) + if (pci_ctrl->mem_resources[i].flags) + bus->resource[i+1] =&pci_ctrl->mem_resources[i]; + pci_ctrl->bus = bus; + pci_ctrl->last_busno = bus->subordinate; + if (next_busno <= pci_ctrl->last_busno) + next_busno = pci_ctrl->last_busno+1; + } + pci_bus_count = next_busno; + + return platform_pcibios_fixup(); +} + +subsys_initcall(pcibios_init); + +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_controller *pci_ctrl = bus->sysdata; + struct resource *res; + unsigned long io_offset; + int i; + + io_offset = (unsigned long)pci_ctrl->io_space.base; + if (bus->parent == NULL) { + /* this is a host bridge - fill in its resources */ + pci_ctrl->bus = bus; + + bus->resource[0] = res = &pci_ctrl->io_resource; + if (!res->flags) { + if (io_offset) + printk (KERN_ERR "I/O resource not set for host" + " bridge %d\n", pci_ctrl->index); + res->start = 0; + res->end = IO_SPACE_LIMIT; + res->flags = IORESOURCE_IO; + } + res->start += io_offset; + res->end += io_offset; + + for (i = 0; i < 3; i++) { + res = &pci_ctrl->mem_resources[i]; + if (!res->flags) { + if (i > 0) + continue; + printk(KERN_ERR "Memory resource not set for " + "host bridge %d\n", pci_ctrl->index); + res->start = 0; + res->end = ~0U; + res->flags = IORESOURCE_MEM; + } + bus->resource[i+1] = res; + } + } else { + /* This is a subordinate bridge */ + pci_read_bridge_bases(bus); + + for (i = 0; i < 4; i++) { + if ((res = bus->resource[i]) == NULL || !res->flags) + continue; + if (io_offset && (res->flags & IORESOURCE_IO)) { + res->start += io_offset; + res->end += io_offset; + } + } + } +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +/* the next one is stolen from the alpha port... */ + +void __init +pcibios_update_irq(struct pci_dev *dev, int irq) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + 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 (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; +} + +#ifdef CONFIG_PROC_FS + +/* + * Return the index of the PCI controller for device pdev. + */ + +int +pci_controller_num(struct pci_dev *dev) +{ + struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata; + return pci_ctrl->index; +} + +#endif /* CONFIG_PROC_FS */ + + +static void +pcibios_fixup_resources(struct pci_dev *dev) +{ + struct pci_controller* pci_ctrl = (struct pci_controller *)dev->sysdata; + int i; + unsigned long offset; + + if (!pci_ctrl) { + printk(KERN_ERR "No pci_ctrl for PCI dev %s!\n",dev->slot_name); + return; + } + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + struct resource *res = dev->resource + i; + if (!res->start || !res->flags) + continue; + if (res->end == 0xffffffff) { + DBG("PCI:%s Resource %d [%08lx-%08lx] is unassigned\n", + dev->slot_name, i, res->start, res->end); + res->end -= res->start; + res->start = 0; + continue; + } + offset = 0; + if (res->flags & IORESOURCE_IO) + offset = (unsigned long) pci_ctrl->io_space.base; + else if (res->flags & IORESOURCE_MEM) + offset = (unsigned long) pci_ctrl->mem_space.base; + + if (offset != 0) { + res->start += offset; + res->end += offset; +#ifdef DEBUG + printk("Fixup res %d (%lx) of dev %s: %lx -> %lx\n", + i, res->flags, dev->slot_name, + res->start - offset, res->start); +#endif + } + } +} + +/* + * 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 *pci_ctrl = (struct pci_controller*) dev->sysdata; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long io_offset = 0; + int i, res_bit; + + if (pci_ctrl == 0) + return -EINVAL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)pci_ctrl->io_space.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 += pci_ctrl->io_space.start - 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) +{ + int prot = pgprot_val(vma->vm_page_prot); + + /* Set to write-through */ + prot &= ~_PAGE_NO_CACHE; +#if 0 + if (!write_combine) + prot |= _PAGE_WRITETHRU; +#endif + 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 = io_remap_page_range(vma, vma->vm_start, vma->vm_pgoff<vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +/* + * This probably belongs here rather than ioport.c because + * we do not want this crud linked into SBus kernels. + * Also, think for a moment about likes of floppy.c that + * include architecture specific parts. They may want to redefine ins/outs. + * + * We do not use horroble macroses here because we want to + * advance pointer by sizeof(size). + */ +void outsb(unsigned long addr, const void *src, unsigned long count) { + while (count) { + count -= 1; + writeb(*(const char *)src, addr); + src += 1; + addr += 1; + } +} + +void outsw(unsigned long addr, const void *src, unsigned long count) { + while (count) { + count -= 2; + writew(*(const short *)src, addr); + src += 2; + addr += 2; + } +} + +void outsl(unsigned long addr, const void *src, unsigned long count) { + while (count) { + count -= 4; + writel(*(const long *)src, addr); + src += 4; + addr += 4; + } +} + +void insb(unsigned long addr, void *dst, unsigned long count) { + while (count) { + count -= 1; + *(unsigned char *)dst = readb(addr); + dst += 1; + addr += 1; + } +} + +void insw(unsigned long addr, void *dst, unsigned long count) { + while (count) { + count -= 2; + *(unsigned short *)dst = readw(addr); + dst += 2; + addr += 2; + } +} + +void insl(unsigned long addr, void *dst, unsigned long count) { + while (count) { + count -= 4; + /* + * XXX I am sure we are in for an unaligned trap here. + */ + *(unsigned long *)dst = readl(addr); + dst += 4; + addr += 4; + } +} + + + diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c new file mode 100644 index 000000000000..cf1362784443 --- /dev/null +++ b/arch/xtensa/kernel/platform.c @@ -0,0 +1,49 @@ +/* + * arch/xtensa/kernel/platform.c + * + * Default platform functions. + * + * 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) 2005 Tensilica Inc. + * + * Chris Zankel + */ + +#include +#include +#include +#include +#include +#include + +#define _F(r,f,a,b) \ + r __platform_##f a b; \ + r platform_##f a __attribute__((weak, alias("__platform_"#f))) + +/* + * Default functions that are used if no platform specific function is defined. + * (Please, refer to include/asm-xtensa/platform.h for more information) + */ + +_F(void, setup, (char** cmd), { }); +_F(void, init_irq, (void), { }); +_F(void, restart, (void), { while(1); }); +_F(void, halt, (void), { while(1); }); +_F(void, power_off, (void), { while(1); }); +_F(void, idle, (void), { __asm__ __volatile__ ("waiti 0" ::: "memory"); }); +_F(void, heartbeat, (void), { }); +_F(int, pcibios_fixup, (void), { return 0; }); +_F(int, get_rtc_time, (time_t* t), { return 0; }); +_F(int, set_rtc_time, (time_t t), { return 0; }); + +#if CONFIG_XTENSA_CALIBRATE_CCOUNT +_F(void, calibrate_ccount, (void), +{ + printk ("ERROR: Cannot calibrate cpu frequency! Assuming 100MHz.\n"); + ccount_per_jiffy = 100 * (1000000UL/HZ); +}); +#endif + diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c new file mode 100644 index 000000000000..4099703b14be --- /dev/null +++ b/arch/xtensa/kernel/process.c @@ -0,0 +1,482 @@ +// TODO verify coprocessor handling +/* + * arch/xtensa/kernel/process.c + * + * Xtensa Processor version. + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Joe Taylor + * Chris Zankel + * Marc Gauthier + * Kevin Chea + */ + +#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 ret_from_fork(void); + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); +EXPORT_SYMBOL(init_mm); + +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = +{ INIT_THREAD_INFO(init_task) }; + +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); + +struct task_struct *current_set[NR_CPUS] = {&init_task, }; + + +#if XCHAL_CP_NUM > 0 + +/* + * Coprocessor ownership. + */ + +coprocessor_info_t coprocessor_info[] = { + { 0, XTENSA_CPE_CP0_OFFSET }, + { 0, XTENSA_CPE_CP1_OFFSET }, + { 0, XTENSA_CPE_CP2_OFFSET }, + { 0, XTENSA_CPE_CP3_OFFSET }, + { 0, XTENSA_CPE_CP4_OFFSET }, + { 0, XTENSA_CPE_CP5_OFFSET }, + { 0, XTENSA_CPE_CP6_OFFSET }, + { 0, XTENSA_CPE_CP7_OFFSET }, +}; + +#endif + +/* + * Powermanagement idle function, if any is provided by the platform. + */ + +void cpu_idle(void) +{ + local_irq_enable(); + + /* endless idle loop with no priority at all */ + while (1) { + while (!need_resched()) + platform_idle(); + preempt_enable(); + schedule(); + } +} + +/* + * Free current thread data structures etc.. + */ + +void exit_thread(void) +{ + release_coprocessors(current); /* Empty macro if no CPs are defined */ +} + +void flush_thread(void) +{ + release_coprocessors(current); /* Empty macro if no CPs are defined */ +} + +/* + * Copy thread. + * + * The stack layout for the new thread looks like this: + * + * +------------------------+ <- sp in childregs (= tos) + * | childregs | + * +------------------------+ <- thread.sp = sp in dummy-frame + * | dummy-frame | (saved in dummy-frame spill-area) + * +------------------------+ + * + * We create a dummy frame to return to ret_from_fork: + * a0 points to ret_from_fork (simulating a call4) + * sp points to itself (thread.sp) + * a2, a3 are unused. + * + * Note: This is a pristine frame, so we don't need any spill region on top of + * childregs. + */ + +int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs *childregs; + unsigned long tos; + int user_mode = user_mode(regs); + + /* Set up new TSS. */ + tos = (unsigned long)p->thread_info + THREAD_SIZE; + if (user_mode) + childregs = (struct pt_regs*)(tos - PT_USER_SIZE); + else + childregs = (struct pt_regs*)tos - 1; + + *childregs = *regs; + + /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */ + *((int*)childregs - 3) = (unsigned long)childregs; + *((int*)childregs - 4) = 0; + + childregs->areg[1] = tos; + childregs->areg[2] = 0; + p->set_child_tid = p->clear_child_tid = NULL; + p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1); + p->thread.sp = (unsigned long)childregs; + if (user_mode(regs)) { + + int len = childregs->wmask & ~0xf; + childregs->areg[1] = usp; + memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], + ®s->areg[XCHAL_NUM_AREGS - len/4], len); + + if (clone_flags & CLONE_SETTLS) + childregs->areg[2] = childregs->areg[6]; + + } else { + /* In kernel space, we start a new thread with a new stack. */ + childregs->wmask = 1; + } + return 0; +} + + +/* + * Create a kernel thread + */ + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + __asm__ __volatile__ + ("mov a5, %4\n\t" /* preserve fn in a5 */ + "mov a6, %3\n\t" /* preserve and setup arg in a6 */ + "movi a2, %1\n\t" /* load __NR_clone for syscall*/ + "mov a3, sp\n\t" /* sp check and sys_clone */ + "mov a4, %5\n\t" /* load flags for syscall */ + "syscall\n\t" + "beq a3, sp, 1f\n\t" /* branch if parent */ + "callx4 a5\n\t" /* call fn */ + "movi a2, %2\n\t" /* load __NR_exit for syscall */ + "mov a3, a6\n\t" /* load fn return value */ + "syscall\n" + "1:\n\t" + "mov %0, a2\n\t" /* parent returns zero */ + :"=r" (retval) + :"i" (__NR_clone), "i" (__NR_exit), + "r" (arg), "r" (fn), + "r" (flags | CLONE_VM) + : "a2", "a3", "a4", "a5", "a6" ); + return retval; +} + + +/* + * These bracket the sleeping functions.. + */ + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long sp, pc; + unsigned long stack_page = (unsigned long) p->thread_info; + int count = 0; + + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + sp = p->thread.sp; + pc = MAKE_PC_FROM_RA(p->thread.ra, p->thread.sp); + + do { + if (sp < stack_page + sizeof(struct task_struct) || + sp >= (stack_page + THREAD_SIZE) || + pc == 0) + return 0; + if (!in_sched_functions(pc)) + return pc; + + /* Stack layout: sp-4: ra, sp-3: sp' */ + + pc = MAKE_PC_FROM_RA(*(unsigned long*)sp - 4, sp); + sp = *(unsigned long *)sp - 3; + } while (count++ < 16); + return 0; +} + +/* + * do_copy_regs() gathers information from 'struct pt_regs' and + * 'current->thread.areg[]' to fill in the xtensa_gregset_t + * structure. + * + * xtensa_gregset_t and 'struct pt_regs' are vastly different formats + * of processor registers. Besides different ordering, + * xtensa_gregset_t contains non-live register information that + * 'struct pt_regs' does not. Exception handling (primarily) uses + * 'struct pt_regs'. Core files and ptrace use xtensa_gregset_t. + * + */ + +void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs, + struct task_struct *tsk) +{ + int i, n, wb_offset; + + elfregs->xchal_config_id0 = XCHAL_HW_CONFIGID0; + elfregs->xchal_config_id1 = XCHAL_HW_CONFIGID1; + + __asm__ __volatile__ ("rsr %0, 176\n" : "=a" (i)); + elfregs->cpux = i; + __asm__ __volatile__ ("rsr %0, 208\n" : "=a" (i)); + elfregs->cpuy = i; + + /* Note: PS.EXCM is not set while user task is running; its + * being set in regs->ps is for exception handling convenience. + */ + + elfregs->pc = regs->pc; + elfregs->ps = (regs->ps & ~XCHAL_PS_EXCM_MASK); + elfregs->exccause = regs->exccause; + elfregs->excvaddr = regs->excvaddr; + elfregs->windowbase = regs->windowbase; + elfregs->windowstart = regs->windowstart; + elfregs->lbeg = regs->lbeg; + elfregs->lend = regs->lend; + elfregs->lcount = regs->lcount; + elfregs->sar = regs->sar; + elfregs->syscall = regs->syscall; + + /* Copy register file. + * The layout looks like this: + * + * | a0 ... a15 | Z ... Z | arX ... arY | + * current window unused saved frames + */ + + memset (elfregs->ar, 0, sizeof(elfregs->ar)); + + wb_offset = regs->windowbase * 4; + n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16; + + for (i = 0; i < n; i++) + elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i]; + + n = (regs->wmask >> 4) * 4; + + for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--) + elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i]; +} + +void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs) +{ + do_copy_regs ((xtensa_gregset_t *)elfregs, regs, current); +} + + +/* The inverse of do_copy_regs(). No error or sanity checking. */ + +void do_restore_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs, + struct task_struct *tsk) +{ + int i, n, wb_offset; + + /* Note: PS.EXCM is not set while user task is running; it + * needs to be set in regs->ps is for exception handling convenience. + */ + + regs->pc = elfregs->pc; + regs->ps = (elfregs->ps | XCHAL_PS_EXCM_MASK); + regs->exccause = elfregs->exccause; + regs->excvaddr = elfregs->excvaddr; + regs->windowbase = elfregs->windowbase; + regs->windowstart = elfregs->windowstart; + regs->lbeg = elfregs->lbeg; + regs->lend = elfregs->lend; + regs->lcount = elfregs->lcount; + regs->sar = elfregs->sar; + regs->syscall = elfregs->syscall; + + /* Clear everything. */ + + memset (regs->areg, 0, sizeof(regs->areg)); + + /* Copy regs from live window frame. */ + + wb_offset = regs->windowbase * 4; + n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16; + + for (i = 0; i < n; i++) + regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i]; + + n = (regs->wmask >> 4) * 4; + + for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--) + regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i]; +} + +/* + * do_save_fpregs() gathers information from 'struct pt_regs' and + * 'current->thread' to fill in the elf_fpregset_t structure. + * + * Core files and ptrace use elf_fpregset_t. + */ + +void do_save_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs, + struct task_struct *tsk) +{ +#if XCHAL_HAVE_CP + + extern unsigned char _xtensa_reginfo_tables[]; + extern unsigned _xtensa_reginfo_table_size; + int i; + unsigned long flags; + + /* Before dumping coprocessor state from memory, + * ensure any live coprocessor contents for this + * task are first saved to memory: + */ + local_irq_save(flags); + + for (i = 0; i < XCHAL_CP_MAX; i++) { + if (tsk == coprocessor_info[i].owner) { + enable_coprocessor(i); + save_coprocessor_registers( + tsk->thread.cp_save+coprocessor_info[i].offset,i); + disable_coprocessor(i); + } + } + + local_irq_restore(flags); + + /* Now dump coprocessor & extra state: */ + memcpy((unsigned char*)fpregs, + _xtensa_reginfo_tables, _xtensa_reginfo_table_size); + memcpy((unsigned char*)fpregs + _xtensa_reginfo_table_size, + tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE); +#endif +} + +/* + * The inverse of do_save_fpregs(). + * Copies coprocessor and extra state from fpregs into regs and tsk->thread. + * Returns 0 on success, non-zero if layout doesn't match. + */ + +int do_restore_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs, + struct task_struct *tsk) +{ +#if XCHAL_HAVE_CP + + extern unsigned char _xtensa_reginfo_tables[]; + extern unsigned _xtensa_reginfo_table_size; + int i; + unsigned long flags; + + /* Make sure save area layouts match. + * FIXME: in the future we could allow restoring from + * a different layout of the same registers, by comparing + * fpregs' table with _xtensa_reginfo_tables and matching + * entries and copying registers one at a time. + * Not too sure yet whether that's very useful. + */ + + if( memcmp((unsigned char*)fpregs, + _xtensa_reginfo_tables, _xtensa_reginfo_table_size) ) { + return -1; + } + + /* Before restoring coprocessor state from memory, + * ensure any live coprocessor contents for this + * task are first invalidated. + */ + + local_irq_save(flags); + + for (i = 0; i < XCHAL_CP_MAX; i++) { + if (tsk == coprocessor_info[i].owner) { + enable_coprocessor(i); + save_coprocessor_registers( + tsk->thread.cp_save+coprocessor_info[i].offset,i); + coprocessor_info[i].owner = 0; + disable_coprocessor(i); + } + } + + local_irq_restore(flags); + + /* Now restore coprocessor & extra state: */ + + memcpy(tsk->thread.cp_save, + (unsigned char*)fpregs + _xtensa_reginfo_table_size, + XTENSA_CP_EXTRA_SIZE); +#endif + return 0; +} +/* + * Fill in the CP structure for a core dump for a particular task. + */ + +int +dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r) +{ +/* see asm/coprocessor.h for this magic number 16 */ +#if TOTAL_CPEXTRA_SIZE > 16 + do_save_fpregs (r, regs, task); + + /* For now, bit 16 means some extra state may be present: */ +// FIXME!! need to track to return more accurate mask + return 0x10000 | XCHAL_CP_MASK; +#else + return 0; /* no coprocessors active on this processor */ +#endif +} + +/* + * Fill in the CP structure for a core dump. + * This includes any FPU coprocessor. + * Here, we dump all coprocessors, and other ("extra") custom state. + * + * This function is called by elf_core_dump() in fs/binfmt_elf.c + * (in which case 'regs' comes from calls to do_coredump, see signals.c). + */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) +{ + return dump_task_fpu(regs, current, r); +} diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c new file mode 100644 index 000000000000..9ef07a4dd2a2 --- /dev/null +++ b/arch/xtensa/kernel/ptrace.c @@ -0,0 +1,407 @@ +// TODO some minor issues +/* + * 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) 2001 - 2005 Tensilica Inc. + * + * Joe Taylor + * Chris Zankel + * Scott Foehner, + * Kevin Chea + * Marc Gauthier + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define TEST_KERNEL // verify kernel operations FIXME: remove + + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ + +void ptrace_disable(struct task_struct *child) +{ + /* Nothing to do.. */ +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + +#if 0 + if ((int)request != 1) + printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n", + (int) request, (int) pid, (unsigned long) addr, + (unsigned long) data); +#endif + + if (request == PTRACE_TRACEME) { + + /* Are we already being traced? */ + + if (current->ptrace & PT_PTRACED) + goto out; + + if ((ret = security_ptrace(current->parent, current))) + 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; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + if ((ret = ptrace_check_attach(child, request == PTRACE_KILL)) < 0) + goto out_tsk; + + switch (request) { + 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); + + goto out; + } + + /* Read the word at location addr in the USER area. */ + + case PTRACE_PEEKUSR: + { + struct pt_regs *regs; + unsigned long tmp; + + regs = xtensa_pt_regs(child); + tmp = 0; /* Default return value. */ + + switch(addr) { + + case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: + { + int ar = addr - REG_AR_BASE - regs->windowbase * 4; + ar &= (XCHAL_NUM_AREGS - 1); + if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0) + tmp = regs->areg[ar]; + else + ret = -EIO; + break; + } + case REG_A_BASE ... REG_A_BASE + 15: + tmp = regs->areg[addr - REG_A_BASE]; + break; + case REG_PC: + tmp = regs->pc; + break; + case REG_PS: + /* Note: PS.EXCM is not set while user task is running; + * its being set in regs is for exception handling + * convenience. */ + tmp = (regs->ps & ~XCHAL_PS_EXCM_MASK); + break; + case REG_WB: + tmp = regs->windowbase; + break; + case REG_WS: + tmp = regs->windowstart; + break; + case REG_LBEG: + tmp = regs->lbeg; + break; + case REG_LEND: + tmp = regs->lend; + break; + case REG_LCOUNT: + tmp = regs->lcount; + break; + case REG_SAR: + tmp = regs->sar; + break; + case REG_DEPC: + tmp = regs->depc; + break; + case REG_EXCCAUSE: + tmp = regs->exccause; + break; + case REG_EXCVADDR: + tmp = regs->excvaddr; + break; + case SYSCALL_NR: + tmp = regs->syscall; + break; + default: + tmp = 0; + ret = -EIO; + goto out; + } + ret = put_user(tmp, (unsigned long *) data); + goto out; + } + + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + if (access_process_vm(child, addr, &data, sizeof(data), 1) + == sizeof(data)) + break; + ret = -EIO; + goto out; + + case PTRACE_POKEUSR: + { + struct pt_regs *regs; + regs = xtensa_pt_regs(child); + + switch (addr) { + case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: + { + int ar = addr - REG_AR_BASE - regs->windowbase * 4; + if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0) + regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data; + else + ret = -EIO; + break; + } + case REG_A_BASE ... REG_A_BASE + 15: + regs->areg[addr - REG_A_BASE] = data; + break; + case REG_PC: + regs->pc = data; + break; + case SYSCALL_NR: + regs->syscall = data; + break; +#ifdef TEST_KERNEL + case REG_WB: + regs->windowbase = data; + break; + case REG_WS: + regs->windowstart = data; + break; +#endif + + default: + /* The rest are not allowed. */ + ret = -EIO; + break; + } + break; + } + + /* continue and stop at next (return from) syscall */ + case PTRACE_SYSCALL: + case PTRACE_CONT: /* restart after signal. */ + { + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + /* Make sure the single step bit is not set. */ + child->ptrace &= ~PT_SINGLESTEP; + 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 == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + child->ptrace &= ~PT_SINGLESTEP; + wake_up_process(child); + break; + + case PTRACE_SINGLESTEP: + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->ptrace |= PT_SINGLESTEP; + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + + case PTRACE_GETREGS: + { + /* 'data' points to user memory in which to write. + * Mainly due to the non-live register values, we + * reformat the register values into something more + * standard. For convenience, we use the handy + * elf_gregset_t format. */ + + xtensa_gregset_t format; + struct pt_regs *regs = xtensa_pt_regs(child); + + do_copy_regs (&format, regs, child); + + /* Now, copy to user space nice and easy... */ + ret = 0; + if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t))) + ret = -EFAULT; + break; + } + + case PTRACE_SETREGS: + { + /* 'data' points to user memory that contains the new + * values in the elf_gregset_t format. */ + + xtensa_gregset_t format; + struct pt_regs *regs = xtensa_pt_regs(child); + + if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){ + ret = -EFAULT; + break; + } + + /* FIXME: Perhaps we want some sanity checks on + * these user-space values? See ARM version. Are + * debuggers a security concern? */ + + do_restore_regs (&format, regs, child); + + ret = 0; + break; + } + + case PTRACE_GETFPREGS: + { + /* 'data' points to user memory in which to write. + * For convenience, we use the handy + * elf_fpregset_t format. */ + + elf_fpregset_t fpregs; + struct pt_regs *regs = xtensa_pt_regs(child); + + do_save_fpregs (&fpregs, regs, child); + + /* Now, copy to user space nice and easy... */ + ret = 0; + if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t))) + ret = -EFAULT; + + break; + } + + case PTRACE_SETFPREGS: + { + /* 'data' points to user memory that contains the new + * values in the elf_fpregset_t format. + */ + elf_fpregset_t fpregs; + struct pt_regs *regs = xtensa_pt_regs(child); + + ret = 0; + if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) { + ret = -EFAULT; + break; + } + + if (do_restore_fpregs (&fpregs, regs, child)) + ret = -EIO; + break; + } + + case PTRACE_GETFPREGSIZE: + /* 'data' points to 'unsigned long' set to the size + * of elf_fpregset_t + */ + ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data); + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + default: + ret = ptrace_request(child, request, addr, data); + goto out; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + + if (!(current->ptrace & PT_PTRACED)) + return; + + /* + * The 0x80 provides a way for the tracing parent to distinguish + * between a syscall stop and SIGTRAP delivery + */ + ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); + + /* + * 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 --git a/arch/xtensa/kernel/semaphore.c b/arch/xtensa/kernel/semaphore.c new file mode 100644 index 000000000000..d40f4b1b75ac --- /dev/null +++ b/arch/xtensa/kernel/semaphore.c @@ -0,0 +1,226 @@ +/* + * arch/xtensa/kernel/semaphore.c + * + * Generic semaphore code. Buyer beware. Do your own specific changes + * in + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Joe Taylor + * Chris Zankel + * Marc Gauthier + * Kevin Chea + */ + +#include +#include +#include +#include +#include + +/* + * These two _must_ execute atomically wrt each other. + */ + +static __inline__ void wake_one_more(struct semaphore * sem) +{ + atomic_inc((atomic_t *)&sem->sleepers); +} + +static __inline__ int waking_non_zero(struct semaphore *sem) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&semaphore_wake_lock, flags); + if (sem->sleepers > 0) { + sem->sleepers--; + ret = 1; + } + spin_unlock_irqrestore(&semaphore_wake_lock, flags); + return ret; +} + +/* + * waking_non_zero_interruptible: + * 1 got the lock + * 0 go to sleep + * -EINTR interrupted + * + * We must undo the sem->count down_interruptible() increment while we are + * protected by the spinlock in order to make atomic this atomic_inc() with the + * atomic_read() in wake_one_more(), otherwise we can race. -arca + */ + +static __inline__ int waking_non_zero_interruptible(struct semaphore *sem, + struct task_struct *tsk) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&semaphore_wake_lock, flags); + if (sem->sleepers > 0) { + sem->sleepers--; + ret = 1; + } else if (signal_pending(tsk)) { + atomic_inc(&sem->count); + ret = -EINTR; + } + spin_unlock_irqrestore(&semaphore_wake_lock, flags); + return ret; +} + +/* + * waking_non_zero_trylock: + * 1 failed to lock + * 0 got the lock + * + * We must undo the sem->count down_trylock() increment while we are + * protected by the spinlock in order to make atomic this atomic_inc() with the + * atomic_read() in wake_one_more(), otherwise we can race. -arca + */ + +static __inline__ int waking_non_zero_trylock(struct semaphore *sem) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&semaphore_wake_lock, flags); + if (sem->sleepers <= 0) + atomic_inc(&sem->count); + else { + sem->sleepers--; + ret = 0; + } + spin_unlock_irqrestore(&semaphore_wake_lock, flags); + return ret; +} + +spinlock_t semaphore_wake_lock; + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waking" variable is + * incremented when the "up()" code goes to wake up waiting + * processes. + * + * 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. + * + * waking_non_zero() (from asm/semaphore.h) must execute + * atomically. + * + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. + * + * This routine adds one to the count of processes that need to + * wake up and exit. ALL waiting processes actually wake up but + * only the one that gets to the "waking" field first will gate + * through and acquire the semaphore. The others will go back + * to sleep. + * + * 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. + */ + +void __up(struct semaphore *sem) +{ + wake_one_more(sem); + wake_up(&sem->wait); +} + +/* + * Perform the "down" function. Return zero for semaphore acquired, + * return negative for signalled out of the function. + * + * If called from __down, the return is ignored and the wait loop is + * not interruptible. This means that a task waiting on a semaphore + * using "down()" cannot be killed until someone does an "up()" on + * the semaphore. + * + * If called from __down_interruptible, the return value gets checked + * upon return. If the return value is negative then the task continues + * with the negative value in the return register (it can be tested by + * the caller). + * + * Either form may be used in conjunction with "up()". + * + */ + +#define DOWN_VAR \ + struct task_struct *tsk = current; \ + wait_queue_t wait; \ + init_waitqueue_entry(&wait, tsk); + +#define DOWN_HEAD(task_state) \ + \ + \ + tsk->state = (task_state); \ + add_wait_queue(&sem->wait, &wait); \ + \ + /* \ + * Ok, we're set up. sem->count is known to be less than zero \ + * so we must wait. \ + * \ + * We can let go the lock for purposes of waiting. \ + * We re-acquire it after awaking so as to protect \ + * all semaphore operations. \ + * \ + * If "up()" is called before we call waking_non_zero() then \ + * we will catch it right away. If it is called later then \ + * we will have to go through a wakeup cycle to catch it. \ + * \ + * Multiple waiters contend for the semaphore lock to see \ + * who gets to gate through and who has to wait some more. \ + */ \ + for (;;) { + +#define DOWN_TAIL(task_state) \ + tsk->state = (task_state); \ + } \ + tsk->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __sched __down(struct semaphore * sem) +{ + DOWN_VAR + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __sched __down_interruptible(struct semaphore * sem) +{ + int ret = 0; + DOWN_VAR + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, tsk); + if (ret) + { + if (ret == 1) + /* ret != 0 only if we get interrupted -arca */ + ret = 0; + break; + } + schedule(); + DOWN_TAIL(TASK_INTERRUPTIBLE) + return ret; +} + +int __down_trylock(struct semaphore * sem) +{ + return waking_non_zero_trylock(sem); +} diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c new file mode 100644 index 000000000000..1f5bf5d624e4 --- /dev/null +++ b/arch/xtensa/kernel/setup.c @@ -0,0 +1,520 @@ +/* + * arch/xtensa/setup.c + * + * 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 Linus Torvalds + * Copyright (C) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * Joe Taylor + * Kevin Chea + * Marc Gauthier + */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) +# include +#endif + +#ifdef CONFIG_RTC +# include +#endif + +#ifdef CONFIG_PROC_FS +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) +struct screen_info screen_info = { 0, 24, 0, 0, 0, 80, 0, 0, 0, 24, 1, 16}; +#endif + +#ifdef CONFIG_BLK_DEV_FD +extern struct fd_ops no_fd_ops; +struct fd_ops *fd_ops; +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +extern struct ide_ops no_ide_ops; +struct ide_ops *ide_ops; +#endif + +extern struct rtc_ops no_rtc_ops; +struct rtc_ops *rtc_ops; + +#ifdef CONFIG_PC_KEYB +extern struct kbd_ops no_kbd_ops; +struct kbd_ops *kbd_ops; +#endif + +#ifdef CONFIG_BLK_DEV_INITRD +extern void *initrd_start; +extern void *initrd_end; +extern void *__initrd_start; +extern void *__initrd_end; +int initrd_is_mapped = 0; +extern int initrd_below_start_ok; +#endif + +unsigned char aux_device_present; +extern unsigned long loops_per_jiffy; + +/* Command line specified as configuration option. */ + +static char command_line[COMMAND_LINE_SIZE]; + +#ifdef CONFIG_CMDLINE_BOOL +static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; +#endif + +sysmem_info_t __initdata sysmem; + +#ifdef CONFIG_BLK_DEV_INITRD +int initrd_is_mapped; +#endif + +extern void init_mmu(void); + +/* + * Boot parameter parsing. + * + * The Xtensa port uses a list of variable-sized tags to pass data to + * the kernel. The first tag must be a BP_TAG_FIRST tag for the list + * to be recognised. The list is terminated with a zero-sized + * BP_TAG_LAST tag. + */ + +typedef struct tagtable { + u32 tag; + int (*parse)(const bp_tag_t*); +} tagtable_t; + +#define __tagtable(tag, fn) static tagtable_t __tagtable_##fn \ + __attribute__((unused, __section__(".taglist"))) = { tag, fn } + +/* parse current tag */ + +static int __init parse_tag_mem(const bp_tag_t *tag) +{ + meminfo_t *mi = (meminfo_t*)(tag->data); + + if (mi->type != MEMORY_TYPE_CONVENTIONAL) + return -1; + + if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) { + printk(KERN_WARNING + "Ignoring memory bank 0x%08lx size %ldKB\n", + (unsigned long)mi->start, + (unsigned long)mi->end - (unsigned long)mi->start); + return -EINVAL; + } + sysmem.bank[sysmem.nr_banks].type = mi->type; + sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start); + sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_SIZE; + sysmem.nr_banks++; + + return 0; +} + +__tagtable(BP_TAG_MEMORY, parse_tag_mem); + +#ifdef CONFIG_BLK_DEV_INITRD + +static int __init parse_tag_initrd(const bp_tag_t* tag) +{ + meminfo_t* mi; + mi = (meminfo_t*)(tag->data); + initrd_start = (void*)(mi->start); + initrd_end = (void*)(mi->end); + + return 0; +} + +__tagtable(BP_TAG_INITRD, parse_tag_initrd); + +#endif /* CONFIG_BLK_DEV_INITRD */ + +static int __init parse_tag_cmdline(const bp_tag_t* tag) +{ + strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE); + command_line[COMMAND_LINE_SIZE - 1] = '\0'; + return 0; +} + +__tagtable(BP_TAG_COMMAND_LINE, parse_tag_cmdline); + +static int __init parse_bootparam(const bp_tag_t* tag) +{ + extern tagtable_t __tagtable_begin, __tagtable_end; + tagtable_t *t; + + /* Boot parameters must start with a BP_TAG_FIRST tag. */ + + if (tag->id != BP_TAG_FIRST) { + printk(KERN_WARNING "Invalid boot parameters!\n"); + return 0; + } + + tag = (bp_tag_t*)((unsigned long)tag + sizeof(bp_tag_t) + tag->size); + + /* Parse all tags. */ + + while (tag != NULL && tag->id != BP_TAG_LAST) { + for (t = &__tagtable_begin; t < &__tagtable_end; t++) { + if (tag->id == t->tag) { + t->parse(tag); + break; + } + } + if (t == &__tagtable_end) + printk(KERN_WARNING "Ignoring tag " + "0x%08x\n", tag->id); + tag = (bp_tag_t*)((unsigned long)(tag + 1) + tag->size); + } + + return 0; +} + +/* + * Initialize architecture. (Early stage) + */ + +void __init init_arch(bp_tag_t *bp_start) +{ + +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = &__initrd_start; + initrd_end = &__initrd_end; +#endif + + sysmem.nr_banks = 0; + +#ifdef CONFIG_CMDLINE_BOOL + strcpy(command_line, default_command_line); +#endif + + /* Parse boot parameters */ + + if (bp_start) + parse_bootparam(bp_start); + + if (sysmem.nr_banks == 0) { + sysmem.nr_banks = 1; + sysmem.bank[0].start = PLATFORM_DEFAULT_MEM_START; + sysmem.bank[0].end = PLATFORM_DEFAULT_MEM_START + + PLATFORM_DEFAULT_MEM_SIZE; + } + + /* Early hook for platforms */ + + platform_init(bp_start); + + /* Initialize MMU. */ + + init_mmu(); +} + +/* + * Initialize system. Setup memory and reserve regions. + */ + +extern char _end; +extern char _stext; +extern char _WindowVectors_text_start; +extern char _WindowVectors_text_end; +extern char _DebugInterruptVector_literal_start; +extern char _DebugInterruptVector_text_end; +extern char _KernelExceptionVector_literal_start; +extern char _KernelExceptionVector_text_end; +extern char _UserExceptionVector_literal_start; +extern char _UserExceptionVector_text_end; +extern char _DoubleExceptionVector_literal_start; +extern char _DoubleExceptionVector_text_end; + +void __init setup_arch(char **cmdline_p) +{ + extern int mem_reserve(unsigned long, unsigned long, int); + extern void bootmem_init(void); + + memcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + *cmdline_p = command_line; + + /* Reserve some memory regions */ + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start < initrd_end) { + initrd_is_mapped = mem_reserve(__pa(initrd_start), + __pa(initrd_end), 0); + initrd_below_start_ok = 1; + } else { + initrd_start = 0; + } +#endif + + mem_reserve(__pa(&_stext),__pa(&_end), 1); + + mem_reserve(__pa(&_WindowVectors_text_start), + __pa(&_WindowVectors_text_end), 0); + + mem_reserve(__pa(&_DebugInterruptVector_literal_start), + __pa(&_DebugInterruptVector_text_end), 0); + + mem_reserve(__pa(&_KernelExceptionVector_literal_start), + __pa(&_KernelExceptionVector_text_end), 0); + + mem_reserve(__pa(&_UserExceptionVector_literal_start), + __pa(&_UserExceptionVector_text_end), 0); + + mem_reserve(__pa(&_DoubleExceptionVector_literal_start), + __pa(&_DoubleExceptionVector_text_end), 0); + + bootmem_init(); + + platform_setup(cmdline_p); + + + paging_init(); + +#ifdef CONFIG_VT +# if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +# elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +# endif +#endif + +#if CONFIG_PCI + platform_pcibios_init(); +#endif +} + +void machine_restart(char * cmd) +{ + platform_restart(); +} + +void machine_halt(void) +{ + platform_halt(); + while (1); +} + +void machine_power_off(void) +{ + platform_power_off(); + while (1); +} +#ifdef CONFIG_PROC_FS + +/* + * Display some core information through /proc/cpuinfo. + */ + +static int +c_show(struct seq_file *f, void *slot) +{ + /* high-level stuff */ + seq_printf(f,"processor\t: 0\n" + "vendor_id\t: Tensilica\n" + "model\t\t: Xtensa " XCHAL_HW_RELEASE_NAME "\n" + "core ID\t\t: " XCHAL_CORE_ID "\n" + "build ID\t: 0x%x\n" + "byte order\t: %s\n" + "cpu MHz\t\t: %lu.%02lu\n" + "bogomips\t: %lu.%02lu\n", + XCHAL_BUILD_UNIQUE_ID, + XCHAL_HAVE_BE ? "big" : "little", + CCOUNT_PER_JIFFY/(1000000/HZ), + (CCOUNT_PER_JIFFY/(10000/HZ)) % 100, + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100); + + seq_printf(f,"flags\t\t: " +#if XCHAL_HAVE_NMI + "nmi " +#endif +#if XCHAL_HAVE_DEBUG + "debug " +# if XCHAL_HAVE_OCD + "ocd " +# endif +#endif +#if XCHAL_HAVE_DENSITY + "density " +#endif +#if XCHAL_HAVE_BOOLEANS + "boolean " +#endif +#if XCHAL_HAVE_LOOPS + "loop " +#endif +#if XCHAL_HAVE_NSA + "nsa " +#endif +#if XCHAL_HAVE_MINMAX + "minmax " +#endif +#if XCHAL_HAVE_SEXT + "sext " +#endif +#if XCHAL_HAVE_CLAMPS + "clamps " +#endif +#if XCHAL_HAVE_MAC16 + "mac16 " +#endif +#if XCHAL_HAVE_MUL16 + "mul16 " +#endif +#if XCHAL_HAVE_MUL32 + "mul32 " +#endif +#if XCHAL_HAVE_MUL32_HIGH + "mul32h " +#endif +#if XCHAL_HAVE_FP + "fpu " +#endif + "\n"); + + /* Registers. */ + seq_printf(f,"physical aregs\t: %d\n" + "misc regs\t: %d\n" + "ibreak\t\t: %d\n" + "dbreak\t\t: %d\n", + XCHAL_NUM_AREGS, + XCHAL_NUM_MISC_REGS, + XCHAL_NUM_IBREAK, + XCHAL_NUM_DBREAK); + + + /* Interrupt. */ + seq_printf(f,"num ints\t: %d\n" + "ext ints\t: %d\n" + "int levels\t: %d\n" + "timers\t\t: %d\n" + "debug level\t: %d\n", + XCHAL_NUM_INTERRUPTS, + XCHAL_NUM_EXTINTERRUPTS, + XCHAL_NUM_INTLEVELS, + XCHAL_NUM_TIMERS, + XCHAL_DEBUGLEVEL); + + /* Coprocessors */ +#if XCHAL_HAVE_CP + seq_printf(f, "coprocessors\t: %d\n", XCHAL_CP_NUM); +#else + seq_printf(f, "coprocessors\t: none\n"); +#endif + + /* {I,D}{RAM,ROM} and XLMI */ + seq_printf(f,"inst ROMs\t: %d\n" + "inst RAMs\t: %d\n" + "data ROMs\t: %d\n" + "data RAMs\t: %d\n" + "XLMI ports\t: %d\n", + XCHAL_NUM_IROM, + XCHAL_NUM_IRAM, + XCHAL_NUM_DROM, + XCHAL_NUM_DRAM, + XCHAL_NUM_XLMI); + + /* Cache */ + seq_printf(f,"icache line size: %d\n" + "icache ways\t: %d\n" + "icache size\t: %d\n" + "icache flags\t: " +#if XCHAL_ICACHE_LINE_LOCKABLE + "lock" +#endif + "\n" + "dcache line size: %d\n" + "dcache ways\t: %d\n" + "dcache size\t: %d\n" + "dcache flags\t: " +#if XCHAL_DCACHE_IS_WRITEBACK + "writeback" +#endif +#if XCHAL_DCACHE_LINE_LOCKABLE + "lock" +#endif + "\n", + XCHAL_ICACHE_LINESIZE, + XCHAL_ICACHE_WAYS, + XCHAL_ICACHE_SIZE, + XCHAL_DCACHE_LINESIZE, + XCHAL_DCACHE_WAYS, + XCHAL_DCACHE_SIZE); + + /* MMU */ + seq_printf(f,"ASID bits\t: %d\n" + "ASID invalid\t: %d\n" + "ASID kernel\t: %d\n" + "rings\t\t: %d\n" + "itlb ways\t: %d\n" + "itlb AR ways\t: %d\n" + "dtlb ways\t: %d\n" + "dtlb AR ways\t: %d\n", + XCHAL_MMU_ASID_BITS, + XCHAL_MMU_ASID_INVALID, + XCHAL_MMU_ASID_KERNEL, + XCHAL_MMU_RINGS, + XCHAL_ITLB_WAYS, + XCHAL_ITLB_ARF_WAYS, + XCHAL_DTLB_WAYS, + XCHAL_DTLB_ARF_WAYS); + + return 0; +} + +/* + * We show only CPU #0 info. + */ +static void * +c_start(struct seq_file *f, loff_t *pos) +{ + return (void *) ((*pos == 0) ? (void *)1 : NULL); +} + +static void * +c_next(struct seq_file *f, void *v, loff_t *pos) +{ + return NULL; +} + +static void +c_stop(struct seq_file *f, void *v) +{ +} + +struct seq_operations cpuinfo_op = +{ + start: c_start, + next: c_next, + stop: c_stop, + show: c_show +}; + +#endif /* CONFIG_PROC_FS */ + diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c new file mode 100644 index 000000000000..df6e1e17b096 --- /dev/null +++ b/arch/xtensa/kernel/signal.c @@ -0,0 +1,713 @@ +// TODO coprocessor stuff +/* + * linux/arch/xtensa/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * + * Joe Taylor + * Chris Zankel + * + * + * + */ + +#include +#include +#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))) + +asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, + struct rusage * ru); +asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); + +extern struct task_struct *coproc_owners[]; + + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ + +int sys_sigsuspend(struct pt_regs *regs) +{ + old_sigset_t mask = (old_sigset_t) regs->areg[3]; + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->areg[2] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys_rt_sigsuspend(struct pt_regs *regs) +{ + sigset_t *unewset = (sigset_t *) regs->areg[4]; + size_t sigsetsize = (size_t) regs->areg[3]; + 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->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->areg[2] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage 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; +} + +asmlinkage int +sys_sigaltstack(struct pt_regs *regs) +{ + const stack_t *uss = (stack_t *) regs->areg[4]; + stack_t *uoss = (stack_t *) regs->areg[3]; + + if (regs->depc > 64) + panic ("Double exception sys_sigreturn\n"); + + + return do_sigaltstack(uss, uoss, regs->areg[1]); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ + struct sigcontext sc; + struct _cpstate cpstate; + unsigned long extramask[_NSIG_WORDS-1]; + unsigned char retcode[6]; + unsigned int reserved[4]; /* Reserved area for chaining */ + unsigned int window[4]; /* Window of 4 registers for initial context */ +}; + +struct rt_sigframe +{ + struct siginfo info; + struct ucontext uc; + struct _cpstate cpstate; + unsigned char retcode[6]; + unsigned int reserved[4]; /* Reserved area for chaining */ + unsigned int window[4]; /* Window of 4 registers for initial context */ +}; + +extern void release_all_cp (struct task_struct *); + + +// FIXME restore_cpextra +static inline int +restore_cpextra (struct _cpstate *buf) +{ +#if 0 + /* The signal handler may have used coprocessors in which + * case they are still enabled. We disable them to force a + * reloading of the original task's CP state by the lazy + * context-switching mechanisms of CP exception handling. + * Also, we essentially discard any coprocessor state that the + * signal handler created. */ + + struct task_struct *tsk = current; + release_all_cp(tsk); + return __copy_from_user(tsk->thread.cpextra, buf, TOTAL_CPEXTRA_SIZE); +#endif + return 0; +} + +/* Note: We don't copy double exception 'tregs', we have to finish double exc. first before we return to signal handler! This dbl.exc.handler might cause another double exception, but I think we are fine as the situation is the same as if we had returned to the signal handerl and got an interrupt immediately... + */ + + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +{ + struct thread_struct *thread; + unsigned int err = 0; + unsigned long ps; + struct _cpstate *buf; + +#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) + COPY(pc); + COPY(depc); + COPY(wmask); + COPY(lbeg); + COPY(lend); + COPY(lcount); + COPY(sar); + COPY(windowbase); + COPY(windowstart); +#undef COPY + + /* For PS, restore only PS.CALLINC. + * Assume that all other bits are either the same as for the signal + * handler, or the user mode value doesn't matter (e.g. PS.OWB). + */ + err |= __get_user(ps, &sc->sc_ps); + regs->ps = (regs->ps & ~XCHAL_PS_CALLINC_MASK) + | (ps & XCHAL_PS_CALLINC_MASK); + + /* Additional corruption checks */ + + if ((regs->windowbase >= (XCHAL_NUM_AREGS/4)) + || ((regs->windowstart & ~((1<<(XCHAL_NUM_AREGS/4)) - 1)) != 0) ) + err = 1; + if ((regs->lcount > 0) + && ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) ) + err = 1; + + /* Restore extended register state. + * See struct thread_struct in processor.h. + */ + thread = ¤t->thread; + + err |= __copy_from_user (regs->areg, sc->sc_areg, XCHAL_NUM_AREGS*4); + err |= __get_user(buf, &sc->sc_cpstate); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_cpextra(buf); + } + + regs->syscall = -1; /* disable syscall checks */ + return err; + +badframe: + return 1; +} + +static inline void +flush_my_cpstate(struct task_struct *tsk) +{ + unsigned long flags; + local_irq_save(flags); + +#if 0 // FIXME + for (i = 0; i < XCHAL_CP_NUM; i++) { + if (tsk == coproc_owners[i]) { + xthal_validate_cp(i); + xthal_save_cpregs(tsk->thread.cpregs_ptr[i], i); + + /* Invalidate and "disown" the cp to allow + * callers the chance to reset cp state in the + * task_struct. */ + + xthal_invalidate_cp(i); + coproc_owners[i] = 0; + } + } +#endif + local_irq_restore(flags); +} + +/* Return codes: + 0: nothing saved + 1: stuff to save, successful + -1: stuff to save, error happened +*/ +static int +save_cpextra (struct _cpstate *buf) +{ +#if (XCHAL_EXTRA_SA_SIZE == 0) && (XCHAL_CP_NUM == 0) + return 0; +#else + + /* FIXME: If a task has never used a coprocessor, there is + * no need to save and restore anything. Tracking this + * information would allow us to optimize this section. + * Perhaps we can use current->used_math or (current->flags & + * PF_USEDFPU) or define a new field in the thread + * structure. */ + + /* We flush any live, task-owned cp state to the task_struct, + * then copy it all to the sigframe. Then we clear all + * cp/extra state in the task_struct, effectively + * clearing/resetting all cp/extra state for the signal + * handler (cp-exception handling will load these new values + * into the cp/extra registers.) This step is important for + * things like a floating-point cp, where the OS must reset + * the FCR to the default rounding mode. */ + + int err = 0; + struct task_struct *tsk = current; + + flush_my_cpstate(tsk); + /* Note that we just copy everything: 'extra' and 'cp' state together.*/ + err |= __copy_to_user(buf, tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE); + memset(tsk->thread.cp_save, 0, XTENSA_CP_EXTRA_SIZE); + +#if (XTENSA_CP_EXTRA_SIZE == 0) +#error Sanity check on memset above, cpextra_size should not be zero. +#endif + + return err ? -1 : 1; +#endif +} + +static int +setup_sigcontext(struct sigcontext *sc, struct _cpstate *cpstate, + struct pt_regs *regs, unsigned long mask) +{ + struct thread_struct *thread; + int err = 0; + +//printk("setup_sigcontext\n"); +#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) + COPY(pc); + COPY(ps); + COPY(depc); + COPY(wmask); + COPY(lbeg); + COPY(lend); + COPY(lcount); + COPY(sar); + COPY(windowbase); + COPY(windowstart); +#undef COPY + + /* Save extended register state. + * See struct thread_struct in processor.h. + */ + thread = ¤t->thread; + err |= __copy_to_user (sc->sc_areg, regs->areg, XCHAL_NUM_AREGS * 4); + err |= save_cpextra(cpstate); + err |= __put_user(err ? NULL : cpstate, &sc->sc_cpstate); + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +asmlinkage int sys_sigreturn(struct pt_regs *regs) +{ + struct sigframe *frame = (struct sigframe *)regs->areg[1]; + sigset_t set; + if (regs->depc > 64) + panic ("Double exception sys_sigreturn\n"); + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_NSIG_WORDS > 1 + && __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->sc)) + goto badframe; + return regs->areg[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) +{ + struct rt_sigframe *frame = (struct rt_sigframe *)regs->areg[1]; + sigset_t set; + stack_t st; + int ret; + if (regs->depc > 64) + { + printk("!!!!!!! DEPC !!!!!!!\n"); + return 0; + } + + 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->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + goto badframe; + ret = regs->areg[2]; + + 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->areg[1]); + + return ret; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +{ + if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + + return (void *)((sp - frame_size) & -16ul); +} + +#define USE_SIGRETURN 0 +#define USE_RT_SIGRETURN 1 + +static int +gen_return_code(unsigned char *codemem, unsigned int use_rt_sigreturn) +{ + unsigned int retcall; + int err = 0; + +#if 0 + /* Ignoring SA_RESTORER for now; it's supposed to be obsolete, + * and the xtensa glibc doesn't use it. + */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->pr = (unsigned long) ka->sa.sa_restorer; + } else +#endif /* 0 */ + { + +#if (__NR_sigreturn > 255) || (__NR_rt_sigreturn > 255) + +/* The 12-bit immediate is really split up within the 24-bit MOVI + * instruction. As long as the above system call numbers fit within + * 8-bits, the following code works fine. See the Xtensa ISA for + * details. + */ + +#error Generating the MOVI instruction below breaks! +#endif + + retcall = use_rt_sigreturn ? __NR_rt_sigreturn : __NR_sigreturn; + +#ifdef __XTENSA_EB__ /* Big Endian version */ + /* Generate instruction: MOVI a2, retcall */ + err |= __put_user(0x22, &codemem[0]); + err |= __put_user(0x0a, &codemem[1]); + err |= __put_user(retcall, &codemem[2]); + /* Generate instruction: SYSCALL */ + err |= __put_user(0x00, &codemem[3]); + err |= __put_user(0x05, &codemem[4]); + err |= __put_user(0x00, &codemem[5]); + +#elif defined __XTENSA_EL__ /* Little Endian version */ + /* Generate instruction: MOVI a2, retcall */ + err |= __put_user(0x22, &codemem[0]); + err |= __put_user(0xa0, &codemem[1]); + err |= __put_user(retcall, &codemem[2]); + /* Generate instruction: SYSCALL */ + err |= __put_user(0x00, &codemem[3]); + err |= __put_user(0x50, &codemem[4]); + err |= __put_user(0x00, &codemem[5]); +#else +#error Must use compiler for Xtensa processors. +#endif + } + + /* Flush generated code out of the data cache */ + + if (err == 0) + __flush_invalidate_cache_range((unsigned long)codemem, 6UL); + + return err; +} + +static void +set_thread_state(struct pt_regs *regs, void *stack, unsigned char *retaddr, + void *handler, unsigned long arg1, void *arg2, void *arg3) +{ + /* Set up registers for signal handler */ + start_thread(regs, (unsigned long) handler, (unsigned long) stack); + + /* Set up a stack frame for a call4 + * Note: PS.CALLINC is set to one by start_thread + */ + regs->areg[4] = (((unsigned long) retaddr) & 0x3fffffff) | 0x40000000; + regs->areg[6] = arg1; + regs->areg[7] = (unsigned long) arg2; + regs->areg[8] = (unsigned long) arg3; +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs *regs) +{ + struct sigframe *frame; + int err = 0; + int signal; + + frame = get_sigframe(ka, regs->areg[1], sizeof(*frame)); + if (regs->depc > 64) + { + printk("!!!!!!! DEPC !!!!!!!\n"); + return; + } + + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= setup_sigcontext(&frame->sc, &frame->cpstate, regs, set->sig[0]); + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + + /* Create sys_sigreturn syscall in stack frame */ + err |= gen_return_code(frame->retcode, USE_SIGRETURN); + + if (err) + goto give_sigsegv; + + /* Create signal handler execution context. + * Return context not modified until this point. + */ + set_thread_state(regs, frame, frame->retcode, + ka->sa.sa_handler, signal, &frame->sc, NULL); + + /* Set access mode to USER_DS. Nomenclature is outdated, but + * functionality is used in uaccess.h + */ + set_fs(USER_DS); + + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): signal=%d sp=%p pc=%08x\n", + current->comm, current->pid, signal, frame, regs->pc); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +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; + int signal; + + frame = get_sigframe(ka, regs->areg[1], sizeof(*frame)); + if (regs->depc > 64) + panic ("Double exception sys_sigreturn\n"); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + 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((void *)current->sas_ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->areg[1]), + &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->cpstate, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Create sys_rt_sigreturn syscall in stack frame */ + err |= gen_return_code(frame->retcode, USE_RT_SIGRETURN); + + if (err) + goto give_sigsegv; + + /* Create signal handler execution context. + * Return context not modified until this point. + */ + set_thread_state(regs, frame, frame->retcode, + ka->sa.sa_handler, signal, &frame->info, &frame->uc); + + /* Set access mode to USER_DS. Nomenclature is outdated, but + * functionality is used in uaccess.h + */ + set_fs(USER_DS); + +#if DEBUG_SIG + printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n", + current->comm, current->pid, signal, frame, regs->pc); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + + + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + int signr; + struct k_sigaction ka; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + /* Are we from a system call? */ + if (regs->syscall >= 0) { + /* If so, check system call restarting.. */ + switch (regs->areg[2]) { + case ERESTARTNOHAND: + case ERESTART_RESTARTBLOCK: + regs->areg[2] = -EINTR; + break; + + case ERESTARTSYS: + if (!(ka.sa.sa_flags & SA_RESTART)) { + regs->areg[2] = -EINTR; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: + regs->areg[2] = regs->syscall; + regs->pc -= 3; + } + } + + if (signr == 0) + return 0; /* no signals delivered */ + + /* Whee! Actually deliver the signal. */ + + /* Set up the stack frame */ + if (ka.sa.sa_flags & SA_SIGINFO) + setup_rt_frame(signr, &ka, &info, oldset, regs); + else + setup_frame(signr, &ka, 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->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, &ka.sa.sa_mask); + sigaddset(¤t->blocked, signr); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + return 1; +} diff --git a/arch/xtensa/kernel/syscalls.c b/arch/xtensa/kernel/syscalls.c new file mode 100644 index 000000000000..abc8ed6c7026 --- /dev/null +++ b/arch/xtensa/kernel/syscalls.c @@ -0,0 +1,418 @@ +/* + * arch/xtensa/kernel/syscall.c + * + * 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) 2001 - 2005 Tensilica Inc. + * Copyright (C) 2000 Silicon Graphics, Inc. + * Copyright (C) 1995 - 2000 by Ralf Baechle + * + * Joe Taylor + * Marc Gauthier + * Chris Zankel + * Kevin Chea + * + */ + +#define DEBUG 0 + +#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 do_syscall_trace(void); +typedef int (*syscall_t)(void *a0,...); +extern int (*do_syscalls)(struct pt_regs *regs, syscall_t fun, + int narg); +extern syscall_t sys_call_table[]; +extern unsigned char sys_narg_table[]; + +/* + * sys_pipe() is the normal C calling standard for creating a pipe. It's not + * the way unix traditional does this, though. + */ + +int sys_pipe(int __user *userfds) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(userfds, fd, 2 * sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* + * 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; +} + +unsigned long old_mmap(unsigned long addr, size_t len, int prot, + int flags, int fd, off_t offset) +{ + return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} + +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); +} + +int sys_fork(struct pt_regs *regs) +{ + return do_fork(SIGCHLD, regs->areg[1], regs, 0, NULL, NULL); +} + +int sys_vfork(struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK|CLONE_VM|SIGCHLD, regs->areg[1], + regs, 0, NULL, NULL); +} + +int sys_clone(struct pt_regs *regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int __user *parent_tidptr, *child_tidptr; + clone_flags = regs->areg[4]; + newsp = regs->areg[3]; + parent_tidptr = (int __user *)regs->areg[5]; + child_tidptr = (int __user *)regs->areg[6]; + if (!newsp) + newsp = regs->areg[1]; + return do_fork(clone_flags,newsp,regs,0,parent_tidptr,child_tidptr); +} + +/* + * sys_execve() executes a new program. + */ + +int sys_execve(struct pt_regs *regs) +{ + int error; + char * filename; + + filename = getname((char *) (long)regs->areg[5]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) (long)regs->areg[3], + (char **) (long)regs->areg[4], regs); + putname(filename); + +out: + return error; +} + +int sys_uname(struct old_utsname * name) +{ + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + return 0; + return -EFAULT; +} + +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; + + 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); + + return error ? -EFAULT : 0; +} + + +/* + * Build the string table for the builtin "poor man's strace". + */ + +#if DEBUG +#define SYSCALL(fun, narg) #fun, +static char *sfnames[] = { +#include "syscalls.h" +}; +#undef SYS +#endif + +void system_call (struct pt_regs *regs) +{ + syscall_t syscall; + unsigned long parm0, parm1, parm2, parm3, parm4, parm5; + int nargs, res; + unsigned int syscallnr; + int ps; + +#if DEBUG + int i; + unsigned long parms[6]; + char *sysname; +#endif + + regs->syscall = regs->areg[2]; + + do_syscall_trace(); + + /* Have to load after syscall_trace because strace + * sometimes changes regs->syscall. + */ + syscallnr = regs->syscall; + + parm0 = parm1 = parm2 = parm3 = parm4 = parm5 = 0; + + /* Restore interrupt level to syscall invoker's. + * If this were in assembly, we wouldn't disable + * interrupts in the first place: + */ + local_save_flags (ps); + local_irq_restore((ps & ~XCHAL_PS_INTLEVEL_MASK) | + (regs->ps & XCHAL_PS_INTLEVEL_MASK) ); + + if (syscallnr > __NR_Linux_syscalls) { + regs->areg[2] = -ENOSYS; + return; + } + + syscall = sys_call_table[syscallnr]; + nargs = sys_narg_table[syscallnr]; + + if (syscall == NULL) { + regs->areg[2] = -ENOSYS; + return; + } + + /* There shouldn't be more than six arguments in the table! */ + + if (nargs > 6) + panic("Internal error - too many syscall arguments (%d)!\n", + nargs); + + /* Linux takes system-call arguments in registers. The ABI + * and Xtensa software conventions require the system-call + * number in a2. If an argument exists in a2, we move it to + * the next available register. Note that for improved + * efficiency, we do NOT shift all parameters down one + * register to maintain the original order. + * + * At best case (zero arguments), we just write the syscall + * number to a2. At worst case (1 to 6 arguments), we move + * the argument in a2 to the next available register, then + * write the syscall number to a2. + * + * For clarity, the following truth table enumerates all + * possibilities. + * + * arguments syscall number arg0, arg1, arg2, arg3, arg4, arg5 + * --------- -------------- ---------------------------------- + * 0 a2 + * 1 a2 a3 + * 2 a2 a4, a3 + * 3 a2 a5, a3, a4 + * 4 a2 a6, a3, a4, a5 + * 5 a2 a7, a3, a4, a5, a6 + * 6 a2 a8, a3, a4, a5, a6, a7 + */ + if (nargs) { + parm0 = regs->areg[nargs+2]; + parm1 = regs->areg[3]; + parm2 = regs->areg[4]; + parm3 = regs->areg[5]; + parm4 = regs->areg[6]; + parm5 = regs->areg[7]; + } else /* nargs == 0 */ + parm0 = (unsigned long) regs; + +#if DEBUG + parms[0] = parm0; + parms[1] = parm1; + parms[2] = parm2; + parms[3] = parm3; + parms[4] = parm4; + parms[5] = parm5; + + sysname = sfnames[syscallnr]; + if (strncmp(sysname, "sys_", 4) == 0) + sysname = sysname + 4; + + printk("\017SYSCALL:I:%x:%d:%s %s(", regs->pc, current->pid, + current->comm, sysname); + for (i = 0; i < nargs; i++) + printk((i>0) ? ", %#lx" : "%#lx", parms[i]); + printk(")\n"); +#endif + + res = syscall((void *)parm0, parm1, parm2, parm3, parm4, parm5); + +#if DEBUG + printk("\017SYSCALL:O:%d:%s %s(",current->pid, current->comm, sysname); + for (i = 0; i < nargs; i++) + printk((i>0) ? ", %#lx" : "%#lx", parms[i]); + if (res < 4096) + printk(") = %d\n", res); + else + printk(") = %#x\n", res); +#endif /* DEBUG */ + + regs->areg[2] = res; + do_syscall_trace(); +} + +/* + * 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 __user *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + ret = -ENOSYS; + + switch (call) { + case SEMOP: + ret = sys_semtimedop (first, (struct sembuf __user *)ptr, + second, NULL); + break; + + case SEMTIMEDOP: + ret = sys_semtimedop (first, (struct sembuf __user *)ptr, + second, (const struct timespec *) fifth); + break; + + case SEMGET: + ret = sys_semget (first, second, third); + break; + + case SEMCTL: { + union semun fourth; + + if (ptr && !get_user(fourth.__pad, (void *__user *) ptr)) + ret = sys_semctl (first, second, third, fourth); + break; + } + + case MSGSND: + ret = sys_msgsnd (first, (struct msgbuf __user*) ptr, + second, third); + break; + + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + + if (ptr && !copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + ret = sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + break; + } + + default: + ret = sys_msgrcv (first, (struct msgbuf __user *) ptr, + second, 0, third); + break; + } + break; + + case MSGGET: + ret = sys_msgget ((key_t) first, second); + break; + + case MSGCTL: + ret = sys_msgctl (first, second, (struct msqid_ds __user*) ptr); + break; + + case SHMAT: { + ulong raddr; + ret = do_shmat (first, (char __user *) ptr, second, &raddr); + + if (!ret) + ret = put_user (raddr, (ulong __user *) third); + + break; + } + + case SHMDT: + ret = sys_shmdt ((char __user *)ptr); + break; + + case SHMGET: + ret = sys_shmget (first, second, third); + break; + + case SHMCTL: + ret = sys_shmctl (first, second, (struct shmid_ds __user*) ptr); + break; + } + return ret; +} + diff --git a/arch/xtensa/kernel/syscalls.h b/arch/xtensa/kernel/syscalls.h new file mode 100644 index 000000000000..5b3f75f50feb --- /dev/null +++ b/arch/xtensa/kernel/syscalls.h @@ -0,0 +1,248 @@ +/* + * arch/xtensa/kernel/syscalls.h + * + * 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, 1996, 1997, 1998 by Ralf Baechle + * Copyright (C) 2001 - 2005 Tensilica Inc. + * + * Changes by Joe Taylor + */ + +/* + * This file is being included twice - once to build a list of all + * syscalls and once to build a table of how many arguments each syscall + * accepts. Syscalls that receive a pointer to the saved registers are + * marked as having zero arguments. + * + * The binary compatibility calls are in a separate list. + * + * Entry '0' used to be system_call. It's removed to disable indirect + * system calls for now so user tasks can't recurse. See mips' + * sys_syscall for a comparable example. + */ + +SYSCALL(0, 0) /* 00 */ + +SYSCALL(sys_exit, 1) +SYSCALL(sys_fork, 0) +SYSCALL(sys_read, 3) +SYSCALL(sys_write, 3) +SYSCALL(sys_open, 3) /* 05 */ +SYSCALL(sys_close, 1) +SYSCALL(sys_waitpid, 3) +SYSCALL(sys_creat, 2) +SYSCALL(sys_link, 2) +SYSCALL(sys_unlink, 1) /* 10 */ +SYSCALL(sys_execve, 0) +SYSCALL(sys_chdir, 1) +SYSCALL(sys_time, 1) +SYSCALL(sys_mknod, 3) +SYSCALL(sys_chmod, 2) /* 15 */ +SYSCALL(sys_lchown, 3) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_stat, 2) +SYSCALL(sys_lseek, 3) +SYSCALL(sys_getpid, 0) /* 20 */ +SYSCALL(sys_mount, 5) +SYSCALL(sys_oldumount, 1) +SYSCALL(sys_setuid, 1) +SYSCALL(sys_getuid, 0) +SYSCALL(sys_stime, 1) /* 25 */ +SYSCALL(sys_ptrace, 4) +SYSCALL(sys_alarm, 1) +SYSCALL(sys_fstat, 2) +SYSCALL(sys_pause, 0) +SYSCALL(sys_utime, 2) /* 30 */ +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_access, 2) +SYSCALL(sys_nice, 1) +SYSCALL(sys_ni_syscall, 0) /* 35 */ +SYSCALL(sys_sync, 0) +SYSCALL(sys_kill, 2) +SYSCALL(sys_rename, 2) +SYSCALL(sys_mkdir, 2) +SYSCALL(sys_rmdir, 1) /* 40 */ +SYSCALL(sys_dup, 1) +SYSCALL(sys_pipe, 1) +SYSCALL(sys_times, 1) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_brk, 1) /* 45 */ +SYSCALL(sys_setgid, 1) +SYSCALL(sys_getgid, 0) +SYSCALL(sys_ni_syscall, 0) /* was signal(2) */ +SYSCALL(sys_geteuid, 0) +SYSCALL(sys_getegid, 0) /* 50 */ +SYSCALL(sys_acct, 1) +SYSCALL(sys_umount, 2) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_ioctl, 3) +SYSCALL(sys_fcntl, 3) /* 55 */ +SYSCALL(sys_ni_syscall, 2) +SYSCALL(sys_setpgid, 2) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_olduname, 1) +SYSCALL(sys_umask, 1) /* 60 */ +SYSCALL(sys_chroot, 1) +SYSCALL(sys_ustat, 2) +SYSCALL(sys_dup2, 2) +SYSCALL(sys_getppid, 0) +SYSCALL(sys_getpgrp, 0) /* 65 */ +SYSCALL(sys_setsid, 0) +SYSCALL(sys_sigaction, 3) +SYSCALL(sys_sgetmask, 0) +SYSCALL(sys_ssetmask, 1) +SYSCALL(sys_setreuid, 2) /* 70 */ +SYSCALL(sys_setregid, 2) +SYSCALL(sys_sigsuspend, 0) +SYSCALL(sys_sigpending, 1) +SYSCALL(sys_sethostname, 2) +SYSCALL(sys_setrlimit, 2) /* 75 */ +SYSCALL(sys_getrlimit, 2) +SYSCALL(sys_getrusage, 2) +SYSCALL(sys_gettimeofday, 2) +SYSCALL(sys_settimeofday, 2) +SYSCALL(sys_getgroups, 2) /* 80 */ +SYSCALL(sys_setgroups, 2) +SYSCALL(sys_ni_syscall, 0) /* old_select */ +SYSCALL(sys_symlink, 2) +SYSCALL(sys_lstat, 2) +SYSCALL(sys_readlink, 3) /* 85 */ +SYSCALL(sys_uselib, 1) +SYSCALL(sys_swapon, 2) +SYSCALL(sys_reboot, 3) +SYSCALL(old_readdir, 3) +SYSCALL(old_mmap, 6) /* 90 */ +SYSCALL(sys_munmap, 2) +SYSCALL(sys_truncate, 2) +SYSCALL(sys_ftruncate, 2) +SYSCALL(sys_fchmod, 2) +SYSCALL(sys_fchown, 3) /* 95 */ +SYSCALL(sys_getpriority, 2) +SYSCALL(sys_setpriority, 3) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_statfs, 2) +SYSCALL(sys_fstatfs, 2) /* 100 */ +SYSCALL(sys_ni_syscall, 3) +SYSCALL(sys_socketcall, 2) +SYSCALL(sys_syslog, 3) +SYSCALL(sys_setitimer, 3) +SYSCALL(sys_getitimer, 2) /* 105 */ +SYSCALL(sys_newstat, 2) +SYSCALL(sys_newlstat, 2) +SYSCALL(sys_newfstat, 2) +SYSCALL(sys_uname, 1) +SYSCALL(sys_ni_syscall, 0) /* 110 */ +SYSCALL(sys_vhangup, 0) +SYSCALL(sys_ni_syscall, 0) /* was sys_idle() */ +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_wait4, 4) +SYSCALL(sys_swapoff, 1) /* 115 */ +SYSCALL(sys_sysinfo, 1) +SYSCALL(sys_ipc, 5) /* 6 really, but glibc uses only 5) */ +SYSCALL(sys_fsync, 1) +SYSCALL(sys_sigreturn, 0) +SYSCALL(sys_clone, 0) /* 120 */ +SYSCALL(sys_setdomainname, 2) +SYSCALL(sys_newuname, 1) +SYSCALL(sys_ni_syscall, 0) /* sys_modify_ldt */ +SYSCALL(sys_adjtimex, 1) +SYSCALL(sys_mprotect, 3) /* 125 */ +SYSCALL(sys_sigprocmask, 3) +SYSCALL(sys_ni_syscall, 2) /* old sys_create_module */ +SYSCALL(sys_init_module, 2) +SYSCALL(sys_delete_module, 1) +SYSCALL(sys_ni_syscall, 1) /* old sys_get_kernel_sysm */ /* 130 */ +SYSCALL(sys_quotactl, 0) +SYSCALL(sys_getpgid, 1) +SYSCALL(sys_fchdir, 1) +SYSCALL(sys_bdflush, 2) +SYSCALL(sys_sysfs, 3) /* 135 */ +SYSCALL(sys_personality, 1) +SYSCALL(sys_ni_syscall, 0) /* for afs_syscall */ +SYSCALL(sys_setfsuid, 1) +SYSCALL(sys_setfsgid, 1) +SYSCALL(sys_llseek, 5) /* 140 */ +SYSCALL(sys_getdents, 3) +SYSCALL(sys_select, 5) +SYSCALL(sys_flock, 2) +SYSCALL(sys_msync, 3) +SYSCALL(sys_readv, 3) /* 145 */ +SYSCALL(sys_writev, 3) +SYSCALL(sys_ni_syscall, 3) +SYSCALL(sys_ni_syscall, 3) +SYSCALL(sys_ni_syscall, 4) /* handled in fast syscall handler. */ +SYSCALL(sys_ni_syscall, 0) /* 150 */ +SYSCALL(sys_getsid, 1) +SYSCALL(sys_fdatasync, 1) +SYSCALL(sys_sysctl, 1) +SYSCALL(sys_mlock, 2) +SYSCALL(sys_munlock, 2) /* 155 */ +SYSCALL(sys_mlockall, 1) +SYSCALL(sys_munlockall, 0) +SYSCALL(sys_sched_setparam,2) +SYSCALL(sys_sched_getparam,2) +SYSCALL(sys_sched_setscheduler,3) /* 160 */ +SYSCALL(sys_sched_getscheduler,1) +SYSCALL(sys_sched_yield,0) +SYSCALL(sys_sched_get_priority_max,1) +SYSCALL(sys_sched_get_priority_min,1) +SYSCALL(sys_sched_rr_get_interval,2) /* 165 */ +SYSCALL(sys_nanosleep,2) +SYSCALL(sys_mremap,4) +SYSCALL(sys_accept, 3) +SYSCALL(sys_bind, 3) +SYSCALL(sys_connect, 3) /* 170 */ +SYSCALL(sys_getpeername, 3) +SYSCALL(sys_getsockname, 3) +SYSCALL(sys_getsockopt, 5) +SYSCALL(sys_listen, 2) +SYSCALL(sys_recv, 4) /* 175 */ +SYSCALL(sys_recvfrom, 6) +SYSCALL(sys_recvmsg, 3) +SYSCALL(sys_send, 4) +SYSCALL(sys_sendmsg, 3) +SYSCALL(sys_sendto, 6) /* 180 */ +SYSCALL(sys_setsockopt, 5) +SYSCALL(sys_shutdown, 2) +SYSCALL(sys_socket, 3) +SYSCALL(sys_socketpair, 4) +SYSCALL(sys_setresuid, 3) /* 185 */ +SYSCALL(sys_getresuid, 3) +SYSCALL(sys_ni_syscall, 5) /* old sys_query_module */ +SYSCALL(sys_poll, 3) +SYSCALL(sys_nfsservctl, 3) +SYSCALL(sys_setresgid, 3) /* 190 */ +SYSCALL(sys_getresgid, 3) +SYSCALL(sys_prctl, 5) +SYSCALL(sys_rt_sigreturn, 0) +SYSCALL(sys_rt_sigaction, 4) +SYSCALL(sys_rt_sigprocmask, 4) /* 195 */ +SYSCALL(sys_rt_sigpending, 2) +SYSCALL(sys_rt_sigtimedwait, 4) +SYSCALL(sys_rt_sigqueueinfo, 3) +SYSCALL(sys_rt_sigsuspend, 0) +SYSCALL(sys_pread64, 5) /* 200 */ +SYSCALL(sys_pwrite64, 5) +SYSCALL(sys_chown, 3) +SYSCALL(sys_getcwd, 2) +SYSCALL(sys_capget, 2) +SYSCALL(sys_capset, 2) /* 205 */ +SYSCALL(sys_sigaltstack, 0) +SYSCALL(sys_sendfile, 4) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_ni_syscall, 0) +SYSCALL(sys_mmap2, 6) /* 210 */ +SYSCALL(sys_truncate64, 2) +SYSCALL(sys_ftruncate64, 2) +SYSCALL(sys_stat64, 2) +SYSCALL(sys_lstat64, 2) +SYSCALL(sys_fstat64, 2) /* 215 */ +SYSCALL(sys_pivot_root, 2) +SYSCALL(sys_mincore, 3) +SYSCALL(sys_madvise, 3) +SYSCALL(sys_getdents64, 3) +SYSCALL(sys_vfork, 0) /* 220 */ diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c new file mode 100644 index 000000000000..e07287db5a40 --- /dev/null +++ b/arch/xtensa/kernel/time.c @@ -0,0 +1,227 @@ +/* + * arch/xtensa/kernel/time.c + * + * Timer and clock support. + * + * 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) 2005 Tensilica Inc. + * + * Chris Zankel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +extern volatile unsigned long wall_jiffies; + +u64 jiffies_64 = INITIAL_JIFFIES; +EXPORT_SYMBOL(jiffies_64); + +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; +EXPORT_SYMBOL(rtc_lock); + + +#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT +unsigned long ccount_per_jiffy; /* per 1/HZ */ +unsigned long ccount_nsec; /* nsec per ccount increment */ +#endif + +unsigned int last_ccount_stamp; +static long last_rtc_update = 0; + +/* + * Scheduler clock - returns current tim in nanosec units. + */ + +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies * (1000000000 / HZ); +} + +static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static struct irqaction timer_irqaction = { + .handler = timer_interrupt, + .flags = SA_INTERRUPT, + .name = "timer", +}; + +void __init time_init(void) +{ + time_t sec_o, sec_n = 0; + + /* The platform must provide a function to calibrate the processor + * speed for the CALIBRATE. + */ + +#if CONFIG_XTENSA_CALIBRATE_CCOUNT + printk("Calibrating CPU frequency "); + platform_calibrate_ccount(); + printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ), + (int)(ccount_per_jiffy/(10000/HZ))%100); +#endif + + /* Set time from RTC (if provided) */ + + if (platform_get_rtc_time(&sec_o) == 0) + while (platform_get_rtc_time(&sec_n)) + if (sec_o != sec_n) + break; + + xtime.tv_nsec = 0; + last_rtc_update = xtime.tv_sec = sec_n; + last_ccount_stamp = get_ccount(); + + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + + /* Initialize the linux timer interrupt. */ + + setup_irq(LINUX_TIMER_INT, &timer_irqaction); + set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY); +} + + +int do_settimeofday(struct timespec *tv) +{ + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + unsigned long ccount; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irq(&xtime_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! + */ + ccount = get_ccount(); + nsec -= (ccount - last_ccount_stamp) * CCOUNT_NSEC; + nsec -= (jiffies - wall_jiffies) * CCOUNT_PER_JIFFY * CCOUNT_NSEC; + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irq(&xtime_lock); + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + + +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long sec, usec, delta, lost, seq; + + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + + delta = get_ccount() - last_ccount_stamp; + sec = xtime.tv_sec; + usec = (xtime.tv_nsec / NSEC_PER_USEC); + + lost = jiffies - wall_jiffies; + + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + + usec += lost * (1000000UL/HZ) + (delta * CCOUNT_NSEC) / NSEC_PER_USEC; + for (; usec >= 1000000; sec++, usec -= 1000000) + ; + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +EXPORT_SYMBOL(do_gettimeofday); + +/* + * The timer interrupt is called HZ times per second. + */ + +irqreturn_t timer_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + + unsigned long next; + + next = get_linux_timer(); + +again: + while ((signed long)(get_ccount() - next) > 0) { + + profile_tick(CPU_PROFILING, regs); +#ifndef CONFIG_SMP + update_process_times(user_mode(regs)); +#endif + + write_seqlock(&xtime_lock); + + last_ccount_stamp = next; + next += CCOUNT_PER_JIFFY; + do_timer (regs); /* Linux handler in kernel/timer.c */ + + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs((xtime.tv_nsec/1000)-(1000000-1000000/HZ))<5000000/HZ && + jiffies - wall_jiffies == 1) { + + if (platform_set_rtc_time(xtime.tv_sec+1) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Do it again in 60 s */ + last_rtc_update += 60; + } + write_sequnlock(&xtime_lock); + } + + /* NOTE: writing CCOMPAREn clears the interrupt. */ + + set_linux_timer (next); + + /* Make sure we didn't miss any tick... */ + + if ((signed long)(get_ccount() - next) > 0) + goto again; + + /* Allow platform to do something usefull (Wdog). */ + + platform_heartbeat(); + + return IRQ_HANDLED; +} + +#ifndef CONFIG_GENERIC_CALIBRATE_DELAY +void __devinit calibrate_delay(void) +{ + loops_per_jiffy = CCOUNT_PER_JIFFY; + printk("Calibrating delay loop (skipped)... " + "%lu.%02lu BogoMIPS preset\n", + loops_per_jiffy/(1000000/HZ), + (loops_per_jiffy/(10000/HZ)) % 100); +} +#endif + diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c new file mode 100644 index 000000000000..804246e743b1 --- /dev/null +++ b/arch/xtensa/kernel/traps.c @@ -0,0 +1,498 @@ +/* + * arch/xtensa/kernel/traps.c + * + * Exception handling. + * + * Derived from code with the following copyrights: + * Copyright (C) 1994 - 1999 by Ralf Baechle + * Modified for R3000 by Paul M. Antoine, 1995, 1996 + * Complete output from die() by Ulf Carlsson, 1998 + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * Essentially rewritten for the Xtensa architecture port. + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + * + * Joe Taylor + * Chris Zankel + * Marc Gauthier + * Kevin Chea + * + * 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 + +#ifdef CONFIG_KGDB +extern int gdb_enter; +extern int return_from_debug_flag; +#endif + +/* + * Machine specific interrupt handlers + */ + +extern void kernel_exception(void); +extern void user_exception(void); + +extern void fast_syscall_kernel(void); +extern void fast_syscall_user(void); +extern void fast_alloca(void); +extern void fast_unaligned(void); +extern void fast_second_level_miss(void); +extern void fast_store_prohibited(void); +extern void fast_coprocessor(void); + +extern void do_illegal_instruction (struct pt_regs*); +extern void do_interrupt (struct pt_regs*); +extern void do_unaligned_user (struct pt_regs*); +extern void do_multihit (struct pt_regs*, unsigned long); +extern void do_page_fault (struct pt_regs*, unsigned long); +extern void do_debug (struct pt_regs*); +extern void system_call (struct pt_regs*); + +/* + * The vector table must be preceded by a save area (which + * implies it must be in RAM, unless one places RAM immediately + * before a ROM and puts the vector at the start of the ROM (!)) + */ + +#define KRNL 0x01 +#define USER 0x02 + +#define COPROCESSOR(x) \ +{ XCHAL_EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor } + +typedef struct { + int cause; + int fast; + void* handler; +} dispatch_init_table_t; + +dispatch_init_table_t __init dispatch_init_table[] = { + +{ XCHAL_EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction}, +{ XCHAL_EXCCAUSE_SYSTEM_CALL, KRNL, fast_syscall_kernel }, +{ XCHAL_EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user }, +{ XCHAL_EXCCAUSE_SYSTEM_CALL, 0, system_call }, +/* XCHAL_EXCCAUSE_INSTRUCTION_FETCH unhandled */ +/* XCHAL_EXCCAUSE_LOAD_STORE_ERROR unhandled*/ +{ XCHAL_EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt }, +{ XCHAL_EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca }, +/* XCHAL_EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */ +/* XCHAL_EXCCAUSE_PRIVILEGED unhandled */ +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION +#ifdef CONFIG_UNALIGNED_USER +{ XCHAL_EXCCAUSE_UNALIGNED, USER, fast_unaligned }, +#else +{ XCHAL_EXCCAUSE_UNALIGNED, 0, do_unaligned_user }, +#endif +{ XCHAL_EXCCAUSE_UNALIGNED, KRNL, fast_unaligned }, +#endif +{ XCHAL_EXCCAUSE_ITLB_MISS, 0, do_page_fault }, +{ XCHAL_EXCCAUSE_ITLB_MISS, USER|KRNL, fast_second_level_miss}, +{ XCHAL_EXCCAUSE_ITLB_MULTIHIT, 0, do_multihit }, +{ XCHAL_EXCCAUSE_ITLB_PRIVILEGE, 0, do_page_fault }, +/* XCHAL_EXCCAUSE_SIZE_RESTRICTION unhandled */ +{ XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE, 0, do_page_fault }, +{ XCHAL_EXCCAUSE_DTLB_MISS, USER|KRNL, fast_second_level_miss}, +{ XCHAL_EXCCAUSE_DTLB_MISS, 0, do_page_fault }, +{ XCHAL_EXCCAUSE_DTLB_MULTIHIT, 0, do_multihit }, +{ XCHAL_EXCCAUSE_DTLB_PRIVILEGE, 0, do_page_fault }, +/* XCHAL_EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */ +{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE, USER|KRNL, fast_store_prohibited }, +{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE, 0, do_page_fault }, +{ XCHAL_EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0, do_page_fault }, +/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */ +#if (XCHAL_CP_MASK & 1) +COPROCESSOR(0), +#endif +#if (XCHAL_CP_MASK & 2) +COPROCESSOR(1), +#endif +#if (XCHAL_CP_MASK & 4) +COPROCESSOR(2), +#endif +#if (XCHAL_CP_MASK & 8) +COPROCESSOR(3), +#endif +#if (XCHAL_CP_MASK & 16) +COPROCESSOR(4), +#endif +#if (XCHAL_CP_MASK & 32) +COPROCESSOR(5), +#endif +#if (XCHAL_CP_MASK & 64) +COPROCESSOR(6), +#endif +#if (XCHAL_CP_MASK & 128) +COPROCESSOR(7), +#endif +{ EXCCAUSE_MAPPED_DEBUG, 0, do_debug }, +{ -1, -1, 0 } + +}; + +/* The exception table serves two functions: + * 1. it contains three dispatch tables (fast_user, fast_kernel, default-c) + * 2. it is a temporary memory buffer for the exception handlers. + */ + +unsigned long exc_table[EXC_TABLE_SIZE/4]; + +void die(const char*, struct pt_regs*, long); + +static inline void +__die_if_kernel(const char *str, struct pt_regs *regs, long err) +{ + if (!user_mode(regs)) + die(str, regs, err); +} + +/* + * Unhandled Exceptions. Kill user task or panic if in kernel space. + */ + +void do_unhandled(struct pt_regs *regs, unsigned long exccause) +{ + __die_if_kernel("Caught unhandled exception - should not happen", + regs, SIGKILL); + + /* If in user mode, send SIGILL signal to current process */ + printk("Caught unhandled exception in '%s' " + "(pid = %d, pc = %#010lx) - should not happen\n" + "\tEXCCAUSE is %ld\n", + current->comm, current->pid, regs->pc, exccause); + force_sig(SIGILL, current); +} + +/* + * Multi-hit exception. This if fatal! + */ + +void do_multihit(struct pt_regs *regs, unsigned long exccause) +{ + die("Caught multihit exception", regs, SIGKILL); +} + +/* + * Level-1 interrupt. + * We currently have no priority encoding. + */ + +unsigned long ignored_level1_interrupts; +extern void do_IRQ(int, struct pt_regs *); + +void do_interrupt (struct pt_regs *regs) +{ + unsigned long intread = get_sr (INTREAD); + unsigned long intenable = get_sr (INTENABLE); + int i, mask; + + /* Handle all interrupts (no priorities). + * (Clear the interrupt before processing, in case it's + * edge-triggered or software-generated) + */ + + for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) { + if (mask & (intread & intenable)) { + set_sr (mask, INTCLEAR); + do_IRQ (i,regs); + } + } +} + +/* + * Illegal instruction. Fatal if in kernel space. + */ + +void +do_illegal_instruction(struct pt_regs *regs) +{ + __die_if_kernel("Illegal instruction in kernel", regs, SIGKILL); + + /* If in user mode, send SIGILL signal to current process. */ + + printk("Illegal Instruction in '%s' (pid = %d, pc = %#010lx)\n", + current->comm, current->pid, regs->pc); + force_sig(SIGILL, current); +} + + +/* + * Handle unaligned memory accesses from user space. Kill task. + * + * If CONFIG_UNALIGNED_USER is not set, we don't allow unaligned memory + * accesses causes from user space. + */ + +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION +#ifndef CONFIG_UNALIGNED_USER +void +do_unaligned_user (struct pt_regs *regs) +{ + siginfo_t info; + + __die_if_kernel("Unhandled unaligned exception in kernel", + regs, SIGKILL); + + current->thread.bad_vaddr = regs->excvaddr; + current->thread.error_code = -3; + printk("Unaligned memory access to %08lx in '%s' " + "(pid = %d, pc = %#010lx)\n", + regs->excvaddr, current->comm, current->pid, regs->pc); + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRALN; + info.si_addr = (void *) regs->excvaddr; + force_sig_info(SIGSEGV, &info, current); + +} +#endif +#endif + +void +do_debug(struct pt_regs *regs) +{ +#ifdef CONFIG_KGDB + /* If remote debugging is configured AND enabled, we give control to + * kgdb. Otherwise, we fall through, perhaps giving control to the + * native debugger. + */ + + if (gdb_enter) { + extern void gdb_handle_exception(struct pt_regs *); + gdb_handle_exception(regs); + return_from_debug_flag = 1; + return; + } +#endif + + __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); + + /* If in user mode, send SIGTRAP signal to current process */ + + force_sig(SIGTRAP, current); +} + + +/* + * Initialize dispatch tables. + * + * The exception vectors are stored compressed the __init section in the + * dispatch_init_table. This function initializes the following three tables + * from that compressed table: + * - fast user first dispatch table for user exceptions + * - fast kernel first dispatch table for kernel exceptions + * - default C-handler C-handler called by the default fast handler. + * + * See vectors.S for more details. + */ + +#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler)) + +void trap_init(void) +{ + int i; + + /* Setup default vectors. */ + + for(i = 0; i < 64; i++) { + set_handler(EXC_TABLE_FAST_USER/4 + i, user_exception); + set_handler(EXC_TABLE_FAST_KERNEL/4 + i, kernel_exception); + set_handler(EXC_TABLE_DEFAULT/4 + i, do_unhandled); + } + + /* Setup specific handlers. */ + + for(i = 0; dispatch_init_table[i].cause >= 0; i++) { + + int fast = dispatch_init_table[i].fast; + int cause = dispatch_init_table[i].cause; + void *handler = dispatch_init_table[i].handler; + + if (fast == 0) + set_handler (EXC_TABLE_DEFAULT/4 + cause, handler); + if (fast && fast & USER) + set_handler (EXC_TABLE_FAST_USER/4 + cause, handler); + if (fast && fast & KRNL) + set_handler (EXC_TABLE_FAST_KERNEL/4 + cause, handler); + } + + /* Initialize EXCSAVE_1 to hold the address of the exception table. */ + + i = (unsigned long)exc_table; + __asm__ __volatile__("wsr %0, "__stringify(EXCSAVE_1)"\n" : : "a" (i)); +} + +/* + * This function dumps the current valid window frame and other base registers. + */ + +void show_regs(struct pt_regs * regs) +{ + int i, wmask; + + wmask = regs->wmask & ~1; + + for (i = 0; i < 32; i++) { + if (wmask & (1 << (i / 4))) + break; + if ((i % 8) == 0) + printk ("\n" KERN_INFO "a%02d: ", i); + printk("%08lx ", regs->areg[i]); + } + printk("\n"); + + printk("pc: %08lx, ps: %08lx, depc: %08lx, excvaddr: %08lx\n", + regs->pc, regs->ps, regs->depc, regs->excvaddr); + printk("lbeg: %08lx, lend: %08lx lcount: %08lx, sar: %08lx\n", + regs->lbeg, regs->lend, regs->lcount, regs->sar); + if (user_mode(regs)) + printk("wb: %08lx, ws: %08lx, wmask: %08lx, syscall: %ld\n", + regs->windowbase, regs->windowstart, regs->wmask, + regs->syscall); +} + +void show_trace(struct task_struct *task, unsigned long *sp) +{ + unsigned long a0, a1, pc; + unsigned long sp_start, sp_end; + + a1 = (unsigned long)sp; + + if (a1 == 0) + __asm__ __volatile__ ("mov %0, a1\n" : "=a"(a1)); + + + sp_start = a1 & ~(THREAD_SIZE-1); + sp_end = sp_start + THREAD_SIZE; + + printk("Call Trace:"); +#ifdef CONFIG_KALLSYMS + printk("\n"); +#endif + spill_registers(); + + while (a1 > sp_start && a1 < sp_end) { + sp = (unsigned long*)a1; + + a0 = *(sp - 4); + a1 = *(sp - 3); + + if (a1 <= (unsigned long) sp) + break; + + pc = MAKE_PC_FROM_RA(a0, a1); + + if (kernel_text_address(pc)) { + printk(" [<%08lx>] ", pc); + print_symbol("%s\n", pc); + } + } + printk("\n"); +} + +/* + * This routine abuses get_user()/put_user() to reference pointers + * with at least a bit of error checking ... + */ + +static int kstack_depth_to_print = 24; + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + int i = 0; + unsigned long *stack; + + if (sp == 0) + __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp)); + + stack = sp; + + printk("\nStack: "); + + for (i = 0; i < kstack_depth_to_print; i++) { + if (kstack_end(sp)) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", *sp++); + } + printk("\n"); + show_trace(task, stack); +} + +void dump_stack(void) +{ + show_stack(current, NULL); +} + +EXPORT_SYMBOL(dump_stack); + + +void show_code(unsigned int *pc) +{ + long i; + + printk("\nCode:"); + + for(i = -3 ; i < 6 ; i++) { + unsigned long insn; + if (__get_user(insn, pc + i)) { + printk(" (Bad address in pc)\n"); + break; + } + printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>')); + } +} + +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; + +void die(const char * str, struct pt_regs * regs, long err) +{ + static int die_counter; + int nl = 0; + + console_verbose(); + spin_lock_irq(&die_lock); + + printk("%s: sig: %ld [#%d]\n", str, err, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); + nl = 1; +#endif + if (nl) + printk("\n"); + show_regs(regs); + if (!user_mode(regs)) + show_stack(NULL, (unsigned long*)regs->areg[1]); + + spin_unlock_irq(&die_lock); + + if (in_interrupt()) + panic("Fatal exception in interrupt"); + + if (panic_on_oops) { + printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(5 * HZ); + panic("Fatal exception"); + } + do_exit(err); +} + + diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S new file mode 100644 index 000000000000..81808f0c6742 --- /dev/null +++ b/arch/xtensa/kernel/vectors.S @@ -0,0 +1,464 @@ +/* + * arch/xtensa/kernel/vectors.S + * + * This file contains all exception vectors (user, kernel, and double), + * as well as the window vectors (overflow and underflow), and the debug + * vector. These are the primary vectors executed by the processor if an + * exception occurs. + * + * 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) 2005 Tensilica, Inc. + * + * Chris Zankel + * + */ + +/* + * We use a two-level table approach. The user and kernel exception vectors + * use a first-level dispatch table to dispatch the exception to a registered + * fast handler or the default handler, if no fast handler was registered. + * The default handler sets up a C-stack and dispatches the exception to a + * registerd C handler in the second-level dispatch table. + * + * Fast handler entry condition: + * + * a0: trashed, original value saved on stack (PT_AREG0) + * a1: a1 + * a2: new stack pointer, original value in depc + * a3: dispatch table + * depc: a2, original value saved on stack (PT_DEPC) + * excsave_1: a3 + * + * The value for PT_DEPC saved to stack also functions as a boolean to + * indicate that the exception is either a double or a regular exception: + * + * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception + * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception + * + * Note: Neither the kernel nor the user exception handler generate literals. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * User exception vector. (Exceptions with PS.UM == 1, PS.EXCM == 0) + * + * We get here when an exception occurred while we were in userland. + * We switch to the kernel stack and jump to the first level handler + * associated to the exception cause. + * + * Note: the saved kernel stack pointer (EXC_TABLE_KSTK) is already + * decremented by PT_USER_SIZE. + */ + + .section .UserExceptionVector.text, "ax" + +ENTRY(_UserExceptionVector) + + xsr a3, EXCSAVE_1 # save a3 and get dispatch table + wsr a2, DEPC # save a2 + l32i a2, a3, EXC_TABLE_KSTK # load kernel stack to a2 + s32i a0, a2, PT_AREG0 # save a0 to ESF + rsr a0, EXCCAUSE # retrieve exception cause + s32i a0, a2, PT_DEPC # mark it as a regular exception + addx4 a0, a0, a3 # find entry in table + l32i a0, a0, EXC_TABLE_FAST_USER # load handler + jx a0 + +/* + * Kernel exception vector. (Exceptions with PS.UM == 0, PS.EXCM == 0) + * + * We get this exception when we were already in kernel space. + * We decrement the current stack pointer (kernel) by PT_SIZE and + * jump to the first-level handler associated with the exception cause. + * + * Note: we need to preserve space for the spill region. + */ + + .section .KernelExceptionVector.text, "ax" + +ENTRY(_KernelExceptionVector) + + xsr a3, EXCSAVE_1 # save a3, and get dispatch table + wsr a2, DEPC # save a2 + addi a2, a1, -16-PT_SIZE # adjust stack pointer + s32i a0, a2, PT_AREG0 # save a0 to ESF + rsr a0, EXCCAUSE # retrieve exception cause + s32i a0, a2, PT_DEPC # mark it as a regular exception + addx4 a0, a0, a3 # find entry in table + l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler address + jx a0 + + +/* + * Double exception vector (Exceptions with PS.EXCM == 1) + * We get this exception when another exception occurs while were are + * already in an exception, such as window overflow/underflow exception, + * or 'expected' exceptions, for example memory exception when we were trying + * to read data from an invalid address in user space. + * + * Note that this vector is never invoked for level-1 interrupts, because such + * interrupts are disabled (masked) when PS.EXCM is set. + * + * We decode the exception and take the appropriate action. However, the + * double exception vector is much more careful, because a lot more error + * cases go through the double exception vector than through the user and + * kernel exception vectors. + * + * Occasionally, the kernel expects a double exception to occur. This usually + * happens when accessing user-space memory with the user's permissions + * (l32e/s32e instructions). The kernel state, though, is not always suitable + * for immediate transfer of control to handle_double, where "normal" exception + * processing occurs. Also in kernel mode, TLB misses can occur if accessing + * vmalloc memory, possibly requiring repair in a double exception handler. + * + * The variable at TABLE_FIXUP offset from the pointer in EXCSAVE_1 doubles as + * a boolean variable and a pointer to a fixup routine. If the variable + * EXC_TABLE_FIXUP is non-zero, this handler jumps to that address. A value of + * zero indicates to use the default kernel/user exception handler. + * There is only one exception, when the value is identical to the exc_table + * label, the kernel is in trouble. This mechanism is used to protect critical + * sections, mainly when the handler writes to the stack to assert the stack + * pointer is valid. Once the fixup/default handler leaves that area, the + * EXC_TABLE_FIXUP variable is reset to the fixup handler or zero. + * + * Procedures wishing to use this mechanism should set EXC_TABLE_FIXUP to the + * nonzero address of a fixup routine before it could cause a double exception + * and reset it before it returns. + * + * Some other things to take care of when a fast exception handler doesn't + * specify a particular fixup handler but wants to use the default handlers: + * + * - The original stack pointer (in a1) must not be modified. The fast + * exception handler should only use a2 as the stack pointer. + * + * - If the fast handler manipulates the stack pointer (in a2), it has to + * register a valid fixup handler and cannot use the default handlers. + * + * - The handler can use any other generic register from a3 to a15, but it + * must save the content of these registers to stack (PT_AREG3...PT_AREGx) + * + * - These registers must be saved before a double exception can occur. + * + * - If we ever implement handling signals while in double exceptions, the + * number of registers a fast handler has saved (excluding a0 and a1) must + * be written to PT_AREG1. (1 if only a3 is used, 2 for a3 and a4, etc. ) + * + * The fixup handlers are special handlers: + * + * - Fixup entry conditions differ from regular exceptions: + * + * a0: DEPC + * a1: a1 + * a2: trashed, original value in EXC_TABLE_DOUBLE_A2 + * a3: exctable + * depc: a0 + * excsave_1: a3 + * + * - When the kernel enters the fixup handler, it still assumes it is in a + * critical section, so EXC_TABLE_FIXUP variable is set to exc_table. + * The fixup handler, therefore, has to re-register itself as the fixup + * handler before it returns from the double exception. + * + * - Fixup handler can share the same exception frame with the fast handler. + * The kernel stack pointer is not changed when entering the fixup handler. + * + * - Fixup handlers can jump to the default kernel and user exception + * handlers. Before it jumps, though, it has to setup a exception frame + * on stack. Because the default handler resets the register fixup handler + * the fixup handler must make sure that the default handler returns to + * it instead of the exception address, so it can re-register itself as + * the fixup handler. + * + * In case of a critical condition where the kernel cannot recover, we jump + * to unrecoverable_exception with the following entry conditions. + * All registers a0...a15 are unchanged from the last exception, except: + * + * a0: last address before we jumped to the unrecoverable_exception. + * excsave_1: a0 + * + * + * See the handle_alloca_user and spill_registers routines for example clients. + * + * FIXME: Note: we currently don't allow signal handling coming from a double + * exception, so the item markt with (*) is not required. + */ + + .section .DoubleExceptionVector.text, "ax" + .begin literal_prefix .DoubleExceptionVector + +ENTRY(_DoubleExceptionVector) + + /* Deliberately destroy excsave (don't assume it's value was valid). */ + + wsr a3, EXCSAVE_1 # save a3 + + /* Check for kernel double exception (usually fatal). */ + + rsr a3, PS + _bbci.l a3, PS_UM_SHIFT, .Lksp + + /* Check if we are currently handling a window exception. */ + /* Note: We don't need to indicate that we enter a critical section. */ + + xsr a0, DEPC # get DEPC, save a0 + + movi a3, XCHAL_WINDOW_VECTORS_VADDR + _bltu a0, a3, .Lfixup + addi a3, a3, XSHAL_WINDOW_VECTORS_SIZE + _bgeu a0, a3, .Lfixup + + /* Window overflow/underflow exception. Get stack pointer. */ + + mov a3, a2 + movi a2, exc_table + l32i a2, a2, EXC_TABLE_KSTK + + /* Check for overflow/underflow exception, jump if overflow. */ + + _bbci.l a0, 6, .Lovfl + + /* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */ + + /* Restart window underflow exception. + * We return to the instruction in user space that caused the window + * underflow exception. Therefore, we change window base to the value + * before we entered the window underflow exception and prepare the + * registers to return as if we were coming from a regular exception + * by changing depc (in a0). + * Note: We can trash the current window frame (a0...a3) and depc! + */ + + wsr a2, DEPC # save stack pointer temporarily + rsr a0, PS + extui a0, a0, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS + wsr a0, WINDOWBASE + rsync + + /* We are now in the previous window frame. Save registers again. */ + + xsr a2, DEPC # save a2 and get stack pointer + s32i a0, a2, PT_AREG0 + + wsr a3, EXCSAVE_1 # save a3 + movi a3, exc_table + + rsr a0, EXCCAUSE + s32i a0, a2, PT_DEPC # mark it as a regular exception + addx4 a0, a0, a3 + l32i a0, a0, EXC_TABLE_FAST_USER + jx a0 + +.Lfixup:/* Check for a fixup handler or if we were in a critical section. */ + + /* a0: depc, a1: a1, a2: a2, a3: trashed, depc: a0, excsave1: a3 */ + + movi a3, exc_table + s32i a2, a3, EXC_TABLE_DOUBLE_SAVE # temporary variable + + /* Enter critical section. */ + + l32i a2, a3, EXC_TABLE_FIXUP + s32i a3, a3, EXC_TABLE_FIXUP + beq a2, a3, .Lunrecoverable_fixup # critical! + beqz a2, .Ldflt # no handler was registered + + /* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave: a3 */ + + jx a2 + +.Ldflt: /* Get stack pointer. */ + + l32i a3, a3, EXC_TABLE_DOUBLE_SAVE + addi a2, a3, -PT_USER_SIZE + +.Lovfl: /* Jump to default handlers. */ + + /* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */ + + xsr a3, DEPC + s32i a0, a2, PT_DEPC + s32i a3, a2, PT_AREG0 + + /* a0: avail, a1: a1, a2: kstk, a3: avail, depc: a2, excsave: a3 */ + + movi a3, exc_table + rsr a0, EXCCAUSE + addx4 a0, a0, a3 + l32i a0, a0, EXC_TABLE_FAST_USER + jx a0 + + /* + * We only allow the ITLB miss exception if we are in kernel space. + * All other exceptions are unexpected and thus unrecoverable! + */ + + .extern fast_second_level_miss_double_kernel + +.Lksp: /* a0: a0, a1: a1, a2: a2, a3: trashed, depc: depc, excsave: a3 */ + + rsr a3, EXCCAUSE + beqi a3, XCHAL_EXCCAUSE_ITLB_MISS, 1f + addi a3, a3, -XCHAL_EXCCAUSE_DTLB_MISS + bnez a3, .Lunrecoverable +1: movi a3, fast_second_level_miss_double_kernel + jx a3 + + /* Critical! We can't handle this situation. PANIC! */ + + .extern unrecoverable_exception + +.Lunrecoverable_fixup: + l32i a2, a3, EXC_TABLE_DOUBLE_SAVE + xsr a0, DEPC + +.Lunrecoverable: + rsr a3, EXCSAVE_1 + wsr a0, EXCSAVE_1 + movi a0, unrecoverable_exception + callx0 a0 + + .end literal_prefix + + +/* + * Debug interrupt vector + * + * There is not much space here, so simply jump to another handler. + * EXCSAVE[DEBUGLEVEL] has been set to that handler. + */ + + .section .DebugInterruptVector.text, "ax" + +ENTRY(_DebugInterruptVector) + xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL + jx a0 + + + +/* Window overflow and underflow handlers. + * The handlers must be 64 bytes apart, first starting with the underflow + * handlers underflow-4 to underflow-12, then the overflow handlers + * overflow-4 to overflow-12. + * + * Note: We rerun the underflow handlers if we hit an exception, so + * we try to access any page that would cause a page fault early. + */ + + .section .WindowVectors.text, "ax" + + +/* 4-Register Window Overflow Vector (Handler) */ + + .align 64 +.global _WindowOverflow4 +_WindowOverflow4: + s32e a0, a5, -16 + s32e a1, a5, -12 + s32e a2, a5, -8 + s32e a3, a5, -4 + rfwo + + +/* 4-Register Window Underflow Vector (Handler) */ + + .align 64 +.global _WindowUnderflow4 +_WindowUnderflow4: + l32e a0, a5, -16 + l32e a1, a5, -12 + l32e a2, a5, -8 + l32e a3, a5, -4 + rfwu + + +/* 8-Register Window Overflow Vector (Handler) */ + + .align 64 +.global _WindowOverflow8 +_WindowOverflow8: + s32e a0, a9, -16 + l32e a0, a1, -12 + s32e a2, a9, -8 + s32e a1, a9, -12 + s32e a3, a9, -4 + s32e a4, a0, -32 + s32e a5, a0, -28 + s32e a6, a0, -24 + s32e a7, a0, -20 + rfwo + +/* 8-Register Window Underflow Vector (Handler) */ + + .align 64 +.global _WindowUnderflow8 +_WindowUnderflow8: + l32e a1, a9, -12 + l32e a0, a9, -16 + l32e a7, a1, -12 + l32e a2, a9, -8 + l32e a4, a7, -32 + l32e a3, a9, -4 + l32e a5, a7, -28 + l32e a6, a7, -24 + l32e a7, a7, -20 + rfwu + + +/* 12-Register Window Overflow Vector (Handler) */ + + .align 64 +.global _WindowOverflow12 +_WindowOverflow12: + s32e a0, a13, -16 + l32e a0, a1, -12 + s32e a1, a13, -12 + s32e a2, a13, -8 + s32e a3, a13, -4 + s32e a4, a0, -48 + s32e a5, a0, -44 + s32e a6, a0, -40 + s32e a7, a0, -36 + s32e a8, a0, -32 + s32e a9, a0, -28 + s32e a10, a0, -24 + s32e a11, a0, -20 + rfwo + +/* 12-Register Window Underflow Vector (Handler) */ + + .align 64 +.global _WindowUnderflow12 +_WindowUnderflow12: + l32e a1, a13, -12 + l32e a0, a13, -16 + l32e a11, a1, -12 + l32e a2, a13, -8 + l32e a4, a11, -48 + l32e a8, a11, -32 + l32e a3, a13, -4 + l32e a5, a11, -44 + l32e a6, a11, -40 + l32e a7, a11, -36 + l32e a9, a11, -28 + l32e a10, a11, -24 + l32e a11, a11, -20 + rfwu + + .text + + diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..476b2b53cd01 --- /dev/null +++ b/arch/xtensa/kernel/vmlinux.lds.S @@ -0,0 +1,341 @@ +/* + * arch/xtensa/kernel/vmlinux.lds.S + * + * Xtensa linker script + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * Marc Gauthier + * Joe Taylor + */ + +#include + +#include +#define _NOCLANGUAGE +#include +#include +OUTPUT_ARCH(xtensa) +ENTRY(_start) + +#if XCHAL_MEMORY_ORDER == XTHAL_BIGENDIAN +jiffies = jiffies_64 + 4; +#else +jiffies = jiffies_64; +#endif + +#define KERNELOFFSET 0x1000 + +/* Note: In the following macros, it would be nice to specify only the + vector name and section kind and construct "sym" and "section" using + CPP concatenation, but that does not work reliably. Concatenating a + string with "." produces an invalid token. CPP will not print a + warning because it thinks this is an assembly file, but it leaves + them as multiple tokens and there may or may not be whitespace + between them. */ + +/* Macro for a relocation entry */ + +#define RELOCATE_ENTRY(sym, section) \ + LONG(sym ## _start); \ + LONG(sym ## _end); \ + LONG(LOADADDR(section)) + +/* Macro to define a section for a vector. + * + * Use of the MIN function catches the types of errors illustrated in + * the following example: + * + * Assume the section .DoubleExceptionVector.literal is completely + * full. Then a programmer adds code to .DoubleExceptionVector.text + * that produces another literal. The final literal position will + * overlay onto the first word of the adjacent code section + * .DoubleExceptionVector.text. (In practice, the literals will + * overwrite the code, and the first few instructions will be + * garbage.) + */ + +#define SECTION_VECTOR(sym, section, addr, max_prevsec_size, prevsec) \ + section addr : AT((MIN(LOADADDR(prevsec) + max_prevsec_size, \ + LOADADDR(prevsec) + SIZEOF(prevsec)) + 3) & ~ 3) \ + { \ + . = ALIGN(4); \ + sym ## _start = ABSOLUTE(.); \ + *(section) \ + sym ## _end = ABSOLUTE(.); \ + } + +/* + * Mapping of input sections to output sections when linking. + */ + +SECTIONS +{ + . = XCHAL_KSEG_CACHED_VADDR + KERNELOFFSET; + /* .text section */ + + _text = .; + _stext = .; + _ftext = .; + + .text : + { + /* The .head.text section must be the first section! */ + *(.head.text) + *(.literal .text) + *(.srom.text) + VMLINUX_SYMBOL(__sched_text_start) = .; + *(.sched.text.literal .sched.text) + VMLINUX_SYMBOL(__sched_text_end) = .; + VMLINUX_SYMBOL(__lock_text_start) = .; + *(.spinlock.text.literal .spinlock.text) + VMLINUX_SYMBOL(__lock_text_end) = .; + + } + _etext = .; + + . = ALIGN(16); + + RODATA + + /* Relocation table */ + + . = ALIGN(16); + __boot_reloc_table_start = ABSOLUTE(.); + + __relocate : { + + RELOCATE_ENTRY(_WindowVectors_text, + .WindowVectors.text); +#if 0 + RELOCATE_ENTRY(_KernelExceptionVector_literal, + .KernelExceptionVector.literal); +#endif + RELOCATE_ENTRY(_KernelExceptionVector_text, + .KernelExceptionVector.text); +#if 0 + RELOCATE_ENTRY(_UserExceptionVector_literal, + .UserExceptionVector.literal); +#endif + RELOCATE_ENTRY(_UserExceptionVector_text, + .UserExceptionVector.text); + RELOCATE_ENTRY(_DoubleExceptionVector_literal, + .DoubleExceptionVector.literal); + RELOCATE_ENTRY(_DoubleExceptionVector_text, + .DoubleExceptionVector.text); + } + __boot_reloc_table_end = ABSOLUTE(.) ; + + .fixup : { *(.fixup) } + + . = ALIGN(16); + + __ex_table : { + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + } + + /* Data section */ + + . = ALIGN(XCHAL_ICACHE_LINESIZE); + _fdata = .; + .data : + { + *(.data) CONSTRUCTORS + . = ALIGN(XCHAL_ICACHE_LINESIZE); + *(.data.cacheline_aligned) + } + + _edata = .; + + /* The initial task */ + . = ALIGN(8192); + .data.init_task : { *(.data.init_task) } + + /* Initialization code and data: */ + + . = ALIGN(1< + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_FD +#include +#endif +#ifdef CONFIG_NET +#include +#endif /* CONFIG_NET */ + + +/* + * String functions + */ +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memchr); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(kernel_thread); + +/* + * gcc internal math functions + */ +extern long long __ashrdi3(long long, int); +extern long long __ashldi3(long long, int); +extern long long __lshrdi3(long long, int); +extern int __divsi3(int, int); +extern int __modsi3(int, int); +extern long long __muldi3(long long, long long); +extern int __mulsi3(int, int); +extern unsigned int __udivsi3(unsigned int, unsigned int); +extern unsigned int __umodsi3(unsigned int, unsigned int); +extern unsigned long long __umoddi3(unsigned long long, unsigned long long); +extern unsigned long long __udivdi3(unsigned long long, unsigned long long); + +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__mulsi3); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(__umodsi3); +EXPORT_SYMBOL(__udivdi3); +EXPORT_SYMBOL(__umoddi3); + +/* + * Semaphore operations + */ +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__down_trylock); +EXPORT_SYMBOL(__up); + +#ifdef CONFIG_NET +/* + * Networking support + */ +EXPORT_SYMBOL(csum_partial_copy_generic); +#endif /* CONFIG_NET */ + +/* + * Architecture-specific symbols + */ +EXPORT_SYMBOL(__xtensa_copy_user); + +/* + * Kernel hacking ... + */ + +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) +// FIXME EXPORT_SYMBOL(screen_info); +#endif + +EXPORT_SYMBOL(get_wchan); + +EXPORT_SYMBOL(outsb); +EXPORT_SYMBOL(outsw); +EXPORT_SYMBOL(outsl); +EXPORT_SYMBOL(insb); +EXPORT_SYMBOL(insw); +EXPORT_SYMBOL(insl); diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile new file mode 100644 index 000000000000..ed935b58e8a4 --- /dev/null +++ b/arch/xtensa/lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Xtensa-specific library files. +# + +lib-y += memcopy.o memset.o checksum.o strcasecmp.o \ + usercopy.o strncpy_user.o strnlen_user.o +lib-$(CONFIG_PCI) += pci-auto.o diff --git a/arch/xtensa/lib/checksum.S b/arch/xtensa/lib/checksum.S new file mode 100644 index 000000000000..e2d64dfd530c --- /dev/null +++ b/arch/xtensa/lib/checksum.S @@ -0,0 +1,410 @@ +/* + * 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 + * + * Xtensa version: Copyright (C) 2001 Tensilica, Inc. by Kevin Chea + * Optimized by Joe Taylor + * + * 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 +#define _ASMLANGUAGE +#include + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +/* + * unsigned int csum_partial(const unsigned char *buf, int len, + * unsigned int sum); + * a2 = buf + * a3 = len + * a4 = sum + * + * This function assumes 2- or 4-byte alignment. Other alignments will fail! + */ + +/* ONES_ADD converts twos-complement math to ones-complement. */ +#define ONES_ADD(sum, val) \ + add sum, sum, val ; \ + bgeu sum, val, 99f ; \ + addi sum, sum, 1 ; \ +99: ; + +.text +ENTRY(csum_partial) + /* + * Experiments with Ethernet and SLIP connections show that buf + * is aligned on either a 2-byte or 4-byte boundary. + */ + entry sp, 32 + extui a5, a2, 0, 2 + bnez a5, 8f /* branch if 2-byte aligned */ + /* Fall-through on common case, 4-byte alignment */ +1: + srli a5, a3, 5 /* 32-byte chunks */ +#if XCHAL_HAVE_LOOPS + loopgtz a5, 2f +#else + beqz a5, 2f + slli a5, a5, 5 + add a5, a5, a2 /* a5 = end of last 32-byte chunk */ +.Loop1: +#endif + l32i a6, a2, 0 + l32i a7, a2, 4 + ONES_ADD(a4, a6) + ONES_ADD(a4, a7) + l32i a6, a2, 8 + l32i a7, a2, 12 + ONES_ADD(a4, a6) + ONES_ADD(a4, a7) + l32i a6, a2, 16 + l32i a7, a2, 20 + ONES_ADD(a4, a6) + ONES_ADD(a4, a7) + l32i a6, a2, 24 + l32i a7, a2, 28 + ONES_ADD(a4, a6) + ONES_ADD(a4, a7) + addi a2, a2, 4*8 +#if !XCHAL_HAVE_LOOPS + blt a2, a5, .Loop1 +#endif +2: + extui a5, a3, 2, 3 /* remaining 4-byte chunks */ +#if XCHAL_HAVE_LOOPS + loopgtz a5, 3f +#else + beqz a5, 3f + slli a5, a5, 2 + add a5, a5, a2 /* a5 = end of last 4-byte chunk */ +.Loop2: +#endif + l32i a6, a2, 0 + ONES_ADD(a4, a6) + addi a2, a2, 4 +#if !XCHAL_HAVE_LOOPS + blt a2, a5, .Loop2 +#endif +3: + _bbci.l a3, 1, 5f /* remaining 2-byte chunk */ + l16ui a6, a2, 0 + ONES_ADD(a4, a6) + addi a2, a2, 2 +5: + _bbci.l a3, 0, 7f /* remaining 1-byte chunk */ +6: l8ui a6, a2, 0 +#ifdef __XTENSA_EB__ + slli a6, a6, 8 /* load byte into bits 8..15 */ +#endif + ONES_ADD(a4, a6) +7: + mov a2, a4 + retw + + /* uncommon case, buf is 2-byte aligned */ +8: + beqz a3, 7b /* branch if len == 0 */ + beqi a3, 1, 6b /* branch if len == 1 */ + + extui a5, a2, 0, 1 + bnez a5, 8f /* branch if 1-byte aligned */ + + l16ui a6, a2, 0 /* common case, len >= 2 */ + ONES_ADD(a4, a6) + addi a2, a2, 2 /* adjust buf */ + addi a3, a3, -2 /* adjust len */ + j 1b /* now buf is 4-byte aligned */ + + /* case: odd-byte aligned, len > 1 + * This case is dog slow, so don't give us an odd address. + * (I don't think this ever happens, but just in case.) + */ +8: + srli a5, a3, 2 /* 4-byte chunks */ +#if XCHAL_HAVE_LOOPS + loopgtz a5, 2f +#else + beqz a5, 2f + slli a5, a5, 2 + add a5, a5, a2 /* a5 = end of last 4-byte chunk */ +.Loop3: +#endif + l8ui a6, a2, 0 /* bits 24..31 */ + l16ui a7, a2, 1 /* bits 8..23 */ + l8ui a8, a2, 3 /* bits 0.. 8 */ +#ifdef __XTENSA_EB__ + slli a6, a6, 24 +#else + slli a8, a8, 24 +#endif + slli a7, a7, 8 + or a7, a7, a6 + or a7, a7, a8 + ONES_ADD(a4, a7) + addi a2, a2, 4 +#if !XCHAL_HAVE_LOOPS + blt a2, a5, .Loop3 +#endif +2: + _bbci.l a3, 1, 3f /* remaining 2-byte chunk, still odd addr */ + l8ui a6, a2, 0 + l8ui a7, a2, 1 +#ifdef __XTENSA_EB__ + slli a6, a6, 8 +#else + slli a7, a7, 8 +#endif + or a7, a7, a6 + ONES_ADD(a4, a7) + addi a2, a2, 2 +3: + j 5b /* branch to handle the remaining byte */ + + + +/* + * Copy from ds while checksumming, otherwise like csum_partial + * + * The macros SRC and DST specify the type of access for the instruction. + * thus we can call a custom exception handler for each access type. + */ + +#define SRC(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .long 9999b, 6001f ; \ + .previous + +#define DST(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .long 9999b, 6002f ; \ + .previous + +/* +unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, + int sum, int *src_err_ptr, int *dst_err_ptr) + a2 = src + a3 = dst + a4 = len + a5 = sum + a6 = src_err_ptr + a7 = dst_err_ptr + a8 = temp + a9 = temp + a10 = temp + a11 = original len for exception handling + a12 = original dst for exception handling + + This function is optimized for 4-byte aligned addresses. Other + alignments work, but not nearly as efficiently. + */ + +ENTRY(csum_partial_copy_generic) + entry sp, 32 + mov a12, a3 + mov a11, a4 + or a10, a2, a3 + + /* We optimize the following alignment tests for the 4-byte + aligned case. Two bbsi.l instructions might seem more optimal + (commented out below). However, both labels 5: and 3: are out + of the imm8 range, so the assembler relaxes them into + equivalent bbci.l, j combinations, which is actually + slower. */ + + extui a9, a10, 0, 2 + beqz a9, 1f /* branch if both are 4-byte aligned */ + bbsi.l a10, 0, 5f /* branch if one address is odd */ + j 3f /* one address is 2-byte aligned */ + +/* _bbsi.l a10, 0, 5f */ /* branch if odd address */ +/* _bbsi.l a10, 1, 3f */ /* branch if 2-byte-aligned address */ + +1: + /* src and dst are both 4-byte aligned */ + srli a10, a4, 5 /* 32-byte chunks */ +#if XCHAL_HAVE_LOOPS + loopgtz a10, 2f +#else + beqz a10, 2f + slli a10, a10, 5 + add a10, a10, a2 /* a10 = end of last 32-byte src chunk */ +.Loop5: +#endif +SRC( l32i a9, a2, 0 ) +SRC( l32i a8, a2, 4 ) +DST( s32i a9, a3, 0 ) +DST( s32i a8, a3, 4 ) + ONES_ADD(a5, a9) + ONES_ADD(a5, a8) +SRC( l32i a9, a2, 8 ) +SRC( l32i a8, a2, 12 ) +DST( s32i a9, a3, 8 ) +DST( s32i a8, a3, 12 ) + ONES_ADD(a5, a9) + ONES_ADD(a5, a8) +SRC( l32i a9, a2, 16 ) +SRC( l32i a8, a2, 20 ) +DST( s32i a9, a3, 16 ) +DST( s32i a8, a3, 20 ) + ONES_ADD(a5, a9) + ONES_ADD(a5, a8) +SRC( l32i a9, a2, 24 ) +SRC( l32i a8, a2, 28 ) +DST( s32i a9, a3, 24 ) +DST( s32i a8, a3, 28 ) + ONES_ADD(a5, a9) + ONES_ADD(a5, a8) + addi a2, a2, 32 + addi a3, a3, 32 +#if !XCHAL_HAVE_LOOPS + blt a2, a10, .Loop5 +#endif +2: + extui a10, a4, 2, 3 /* remaining 4-byte chunks */ + extui a4, a4, 0, 2 /* reset len for general-case, 2-byte chunks */ +#if XCHAL_HAVE_LOOPS + loopgtz a10, 3f +#else + beqz a10, 3f + slli a10, a10, 2 + add a10, a10, a2 /* a10 = end of last 4-byte src chunk */ +.Loop6: +#endif +SRC( l32i a9, a2, 0 ) +DST( s32i a9, a3, 0 ) + ONES_ADD(a5, a9) + addi a2, a2, 4 + addi a3, a3, 4 +#if !XCHAL_HAVE_LOOPS + blt a2, a10, .Loop6 +#endif +3: + /* + Control comes to here in two cases: (1) It may fall through + to here from the 4-byte alignment case to process, at most, + one 2-byte chunk. (2) It branches to here from above if + either src or dst is 2-byte aligned, and we process all bytes + here, except for perhaps a trailing odd byte. It's + inefficient, so align your addresses to 4-byte boundaries. + + a2 = src + a3 = dst + a4 = len + a5 = sum + */ + srli a10, a4, 1 /* 2-byte chunks */ +#if XCHAL_HAVE_LOOPS + loopgtz a10, 4f +#else + beqz a10, 4f + slli a10, a10, 1 + add a10, a10, a2 /* a10 = end of last 2-byte src chunk */ +.Loop7: +#endif +SRC( l16ui a9, a2, 0 ) +DST( s16i a9, a3, 0 ) + ONES_ADD(a5, a9) + addi a2, a2, 2 + addi a3, a3, 2 +#if !XCHAL_HAVE_LOOPS + blt a2, a10, .Loop7 +#endif +4: + /* This section processes a possible trailing odd byte. */ + _bbci.l a4, 0, 8f /* 1-byte chunk */ +SRC( l8ui a9, a2, 0 ) +DST( s8i a9, a3, 0 ) +#ifdef __XTENSA_EB__ + slli a9, a9, 8 /* shift byte to bits 8..15 */ +#endif + ONES_ADD(a5, a9) +8: + mov a2, a5 + retw + +5: + /* Control branch to here when either src or dst is odd. We + process all bytes using 8-bit accesses. Grossly inefficient, + so don't feed us an odd address. */ + + srli a10, a4, 1 /* handle in pairs for 16-bit csum */ +#if XCHAL_HAVE_LOOPS + loopgtz a10, 6f +#else + beqz a10, 6f + slli a10, a10, 1 + add a10, a10, a2 /* a10 = end of last odd-aligned, 2-byte src chunk */ +.Loop8: +#endif +SRC( l8ui a9, a2, 0 ) +SRC( l8ui a8, a2, 1 ) +DST( s8i a9, a3, 0 ) +DST( s8i a8, a3, 1 ) +#ifdef __XTENSA_EB__ + slli a9, a9, 8 /* combine into a single 16-bit value */ +#else /* for checksum computation */ + slli a8, a8, 8 +#endif + or a9, a9, a8 + ONES_ADD(a5, a9) + addi a2, a2, 2 + addi a3, a3, 2 +#if !XCHAL_HAVE_LOOPS + blt a2, a10, .Loop8 +#endif +6: + j 4b /* process the possible trailing odd byte */ + + +# Exception handler: +.section .fixup, "ax" +/* + a6 = src_err_ptr + a7 = dst_err_ptr + a11 = original len for exception handling + a12 = original dst for exception handling +*/ + +6001: + _movi a2, -EFAULT + s32i a2, a6, 0 /* src_err_ptr */ + + # clear the complete destination - computing the rest + # is too much work + movi a2, 0 +#if XCHAL_HAVE_LOOPS + loopgtz a11, 2f +#else + beqz a11, 2f + add a11, a11, a12 /* a11 = ending address */ +.Leloop: +#endif + s8i a2, a12, 0 + addi a12, a12, 1 +#if !XCHAL_HAVE_LOOPS + blt a12, a11, .Leloop +#endif +2: + retw + +6002: + movi a2, -EFAULT + s32i a2, a7, 0 /* dst_err_ptr */ + movi a2, 0 + retw + +.previous + diff --git a/arch/xtensa/lib/memcopy.S b/arch/xtensa/lib/memcopy.S new file mode 100644 index 000000000000..e8f6d7eb7222 --- /dev/null +++ b/arch/xtensa/lib/memcopy.S @@ -0,0 +1,315 @@ +/* + * arch/xtensa/lib/hal/memcopy.S -- Core HAL library functions + * xthal_memcpy and xthal_bcopy + * + * 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) 2002 - 2005 Tensilica Inc. + */ + +#include + + .macro src_b r, w0, w1 +#ifdef __XTENSA_EB__ + src \r, \w0, \w1 +#else + src \r, \w1, \w0 +#endif + .endm + + .macro ssa8 r +#ifdef __XTENSA_EB__ + ssa8b \r +#else + ssa8l \r +#endif + .endm + + +/* + * void *memcpy(void *dst, const void *src, size_t len); + * void *memmove(void *dst, const void *src, size_t len); + * void *bcopy(const void *src, void *dst, size_t len); + * + * This function is intended to do the same thing as the standard + * library function memcpy() (or bcopy()) for most cases. + * However, where the source and/or destination references + * an instruction RAM or ROM or a data RAM or ROM, that + * source and/or destination will always be accessed with + * 32-bit load and store instructions (as required for these + * types of devices). + * + * !!!!!!! XTFIXME: + * !!!!!!! Handling of IRAM/IROM has not yet + * !!!!!!! been implemented. + * + * The bcopy version is provided here to avoid the overhead + * of an extra call, for callers that require this convention. + * + * The (general case) algorithm is as follows: + * If destination is unaligned, align it by conditionally + * copying 1 and 2 bytes. + * If source is aligned, + * do 16 bytes with a loop, and then finish up with + * 8, 4, 2, and 1 byte copies conditional on the length; + * else (if source is unaligned), + * do the same, but use SRC to align the source data. + * This code tries to use fall-through branches for the common + * case of aligned source and destination and multiple + * of 4 (or 8) length. + * + * Register use: + * a0/ return address + * a1/ stack pointer + * a2/ return value + * a3/ src + * a4/ length + * a5/ dst + * a6/ tmp + * a7/ tmp + * a8/ tmp + * a9/ tmp + * a10/ tmp + * a11/ tmp + */ + + .text + .align 4 + .global bcopy + .type bcopy,@function +bcopy: + entry sp, 16 # minimal stack frame + # a2=src, a3=dst, a4=len + mov a5, a3 # copy dst so that a2 is return value + mov a3, a2 + mov a2, a5 + j .Lcommon # go to common code for memcpy+bcopy + + +/* + * Byte by byte copy + */ + .align 4 + .byte 0 # 1 mod 4 alignment for LOOPNEZ + # (0 mod 4 alignment for LBEG) +.Lbytecopy: +#if XCHAL_HAVE_LOOPS + loopnez a4, .Lbytecopydone +#else /* !XCHAL_HAVE_LOOPS */ + beqz a4, .Lbytecopydone + add a7, a3, a4 # a7 = end address for source +#endif /* !XCHAL_HAVE_LOOPS */ +.Lnextbyte: + l8ui a6, a3, 0 + addi a3, a3, 1 + s8i a6, a5, 0 + addi a5, a5, 1 +#if !XCHAL_HAVE_LOOPS + blt a3, a7, .Lnextbyte +#endif /* !XCHAL_HAVE_LOOPS */ +.Lbytecopydone: + retw + +/* + * Destination is unaligned + */ + + .align 4 +.Ldst1mod2: # dst is only byte aligned + _bltui a4, 7, .Lbytecopy # do short copies byte by byte + + # copy 1 byte + l8ui a6, a3, 0 + addi a3, a3, 1 + addi a4, a4, -1 + s8i a6, a5, 0 + addi a5, a5, 1 + _bbci.l a5, 1, .Ldstaligned # if dst is now aligned, then + # return to main algorithm +.Ldst2mod4: # dst 16-bit aligned + # copy 2 bytes + _bltui a4, 6, .Lbytecopy # do short copies byte by byte + l8ui a6, a3, 0 + l8ui a7, a3, 1 + addi a3, a3, 2 + addi a4, a4, -2 + s8i a6, a5, 0 + s8i a7, a5, 1 + addi a5, a5, 2 + j .Ldstaligned # dst is now aligned, return to main algorithm + + .align 4 + .global memcpy + .type memcpy,@function +memcpy: + .global memmove + .type memmove,@function +memmove: + + entry sp, 16 # minimal stack frame + # a2/ dst, a3/ src, a4/ len + mov a5, a2 # copy dst so that a2 is return value +.Lcommon: + _bbsi.l a2, 0, .Ldst1mod2 # if dst is 1 mod 2 + _bbsi.l a2, 1, .Ldst2mod4 # if dst is 2 mod 4 +.Ldstaligned: # return here from .Ldst?mod? once dst is aligned + srli a7, a4, 4 # number of loop iterations with 16B + # per iteration + movi a8, 3 # if source is not aligned, + _bany a3, a8, .Lsrcunaligned # then use shifting copy + /* + * Destination and source are word-aligned, use word copy. + */ + # copy 16 bytes per iteration for word-aligned dst and word-aligned src +#if XCHAL_HAVE_LOOPS + loopnez a7, .Loop1done +#else /* !XCHAL_HAVE_LOOPS */ + beqz a7, .Loop1done + slli a8, a7, 4 + add a8, a8, a3 # a8 = end of last 16B source chunk +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop1: + l32i a6, a3, 0 + l32i a7, a3, 4 + s32i a6, a5, 0 + l32i a6, a3, 8 + s32i a7, a5, 4 + l32i a7, a3, 12 + s32i a6, a5, 8 + addi a3, a3, 16 + s32i a7, a5, 12 + addi a5, a5, 16 +#if !XCHAL_HAVE_LOOPS + blt a3, a8, .Loop1 +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop1done: + bbci.l a4, 3, .L2 + # copy 8 bytes + l32i a6, a3, 0 + l32i a7, a3, 4 + addi a3, a3, 8 + s32i a6, a5, 0 + s32i a7, a5, 4 + addi a5, a5, 8 +.L2: + bbsi.l a4, 2, .L3 + bbsi.l a4, 1, .L4 + bbsi.l a4, 0, .L5 + retw +.L3: + # copy 4 bytes + l32i a6, a3, 0 + addi a3, a3, 4 + s32i a6, a5, 0 + addi a5, a5, 4 + bbsi.l a4, 1, .L4 + bbsi.l a4, 0, .L5 + retw +.L4: + # copy 2 bytes + l16ui a6, a3, 0 + addi a3, a3, 2 + s16i a6, a5, 0 + addi a5, a5, 2 + bbsi.l a4, 0, .L5 + retw +.L5: + # copy 1 byte + l8ui a6, a3, 0 + s8i a6, a5, 0 + retw + +/* + * Destination is aligned, Source is unaligned + */ + + .align 4 +.Lsrcunaligned: + _beqz a4, .Ldone # avoid loading anything for zero-length copies + # copy 16 bytes per iteration for word-aligned dst and unaligned src + ssa8 a3 # set shift amount from byte offset +#define SIM_CHECKS_ALIGNMENT 1 /* set to 1 when running on ISS (simulator) with the + lint or ferret client, or 0 to save a few cycles */ +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || SIM_CHECKS_ALIGNMENT + and a11, a3, a8 # save unalignment offset for below + sub a3, a3, a11 # align a3 +#endif + l32i a6, a3, 0 # load first word +#if XCHAL_HAVE_LOOPS + loopnez a7, .Loop2done +#else /* !XCHAL_HAVE_LOOPS */ + beqz a7, .Loop2done + slli a10, a7, 4 + add a10, a10, a3 # a10 = end of last 16B source chunk +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop2: + l32i a7, a3, 4 + l32i a8, a3, 8 + src_b a6, a6, a7 + s32i a6, a5, 0 + l32i a9, a3, 12 + src_b a7, a7, a8 + s32i a7, a5, 4 + l32i a6, a3, 16 + src_b a8, a8, a9 + s32i a8, a5, 8 + addi a3, a3, 16 + src_b a9, a9, a6 + s32i a9, a5, 12 + addi a5, a5, 16 +#if !XCHAL_HAVE_LOOPS + blt a3, a10, .Loop2 +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop2done: + bbci.l a4, 3, .L12 + # copy 8 bytes + l32i a7, a3, 4 + l32i a8, a3, 8 + src_b a6, a6, a7 + s32i a6, a5, 0 + addi a3, a3, 8 + src_b a7, a7, a8 + s32i a7, a5, 4 + addi a5, a5, 8 + mov a6, a8 +.L12: + bbci.l a4, 2, .L13 + # copy 4 bytes + l32i a7, a3, 4 + addi a3, a3, 4 + src_b a6, a6, a7 + s32i a6, a5, 0 + addi a5, a5, 4 + mov a6, a7 +.L13: +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || SIM_CHECKS_ALIGNMENT + add a3, a3, a11 # readjust a3 with correct misalignment +#endif + bbsi.l a4, 1, .L14 + bbsi.l a4, 0, .L15 +.Ldone: retw +.L14: + # copy 2 bytes + l8ui a6, a3, 0 + l8ui a7, a3, 1 + addi a3, a3, 2 + s8i a6, a5, 0 + s8i a7, a5, 1 + addi a5, a5, 2 + bbsi.l a4, 0, .L15 + retw +.L15: + # copy 1 byte + l8ui a6, a3, 0 + s8i a6, a5, 0 + retw + +/* + * Local Variables: + * mode:fundamental + * comment-start: "# " + * comment-start-skip: "# *" + * End: + */ diff --git a/arch/xtensa/lib/memset.S b/arch/xtensa/lib/memset.S new file mode 100644 index 000000000000..4de25134bc62 --- /dev/null +++ b/arch/xtensa/lib/memset.S @@ -0,0 +1,160 @@ +/* + * arch/xtensa/lib/memset.S + * + * ANSI C standard library function memset + * (Well, almost. .fixup code might return zero.) + * + * 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) 2002 Tensilica Inc. + */ + +#include + +/* + * void *memset(void *dst, int c, size_t length) + * + * The algorithm is as follows: + * Create a word with c in all byte positions + * If the destination is aligned, + * do 16B chucks with a loop, and then finish up with + * 8B, 4B, 2B, and 1B stores conditional on the length. + * If destination is unaligned, align it by conditionally + * setting 1B and 2B and then go to aligned case. + * This code tries to use fall-through branches for the common + * case of an aligned destination (except for the branches to + * the alignment labels). + */ + +/* Load or store instructions that may cause exceptions use the EX macro. */ + +#define EX(insn,reg1,reg2,offset,handler) \ +9: insn reg1, reg2, offset; \ + .section __ex_table, "a"; \ + .word 9b, handler; \ + .previous + + +.text +.align 4 +.global memset +.type memset,@function +memset: + entry sp, 16 # minimal stack frame + # a2/ dst, a3/ c, a4/ length + extui a3, a3, 0, 8 # mask to just 8 bits + slli a7, a3, 8 # duplicate character in all bytes of word + or a3, a3, a7 # ... + slli a7, a3, 16 # ... + or a3, a3, a7 # ... + mov a5, a2 # copy dst so that a2 is return value + movi a6, 3 # for alignment tests + bany a2, a6, .Ldstunaligned # if dst is unaligned +.L0: # return here from .Ldstunaligned when dst is aligned + srli a7, a4, 4 # number of loop iterations with 16B + # per iteration + bnez a4, .Laligned + retw + +/* + * Destination is word-aligned. + */ + # set 16 bytes per iteration for word-aligned dst + .align 4 # 1 mod 4 alignment for LOOPNEZ + .byte 0 # (0 mod 4 alignment for LBEG) +.Laligned: +#if XCHAL_HAVE_LOOPS + loopnez a7, .Loop1done +#else /* !XCHAL_HAVE_LOOPS */ + beqz a7, .Loop1done + slli a6, a7, 4 + add a6, a6, a5 # a6 = end of last 16B chunk +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop1: + EX(s32i, a3, a5, 0, memset_fixup) + EX(s32i, a3, a5, 4, memset_fixup) + EX(s32i, a3, a5, 8, memset_fixup) + EX(s32i, a3, a5, 12, memset_fixup) + addi a5, a5, 16 +#if !XCHAL_HAVE_LOOPS + blt a5, a6, .Loop1 +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop1done: + bbci.l a4, 3, .L2 + # set 8 bytes + EX(s32i, a3, a5, 0, memset_fixup) + EX(s32i, a3, a5, 4, memset_fixup) + addi a5, a5, 8 +.L2: + bbci.l a4, 2, .L3 + # set 4 bytes + EX(s32i, a3, a5, 0, memset_fixup) + addi a5, a5, 4 +.L3: + bbci.l a4, 1, .L4 + # set 2 bytes + EX(s16i, a3, a5, 0, memset_fixup) + addi a5, a5, 2 +.L4: + bbci.l a4, 0, .L5 + # set 1 byte + EX(s8i, a3, a5, 0, memset_fixup) +.L5: +.Lret1: + retw + +/* + * Destination is unaligned + */ + +.Ldstunaligned: + bltui a4, 8, .Lbyteset # do short copies byte by byte + bbci.l a5, 0, .L20 # branch if dst alignment half-aligned + # dst is only byte aligned + # set 1 byte + EX(s8i, a3, a5, 0, memset_fixup) + addi a5, a5, 1 + addi a4, a4, -1 + # now retest if dst aligned + bbci.l a5, 1, .L0 # if now aligned, return to main algorithm +.L20: + # dst half-aligned + # set 2 bytes + EX(s16i, a3, a5, 0, memset_fixup) + addi a5, a5, 2 + addi a4, a4, -2 + j .L0 # dst is now aligned, return to main algorithm + +/* + * Byte by byte set + */ + .align 4 + .byte 0 # 1 mod 4 alignment for LOOPNEZ + # (0 mod 4 alignment for LBEG) +.Lbyteset: +#if XCHAL_HAVE_LOOPS + loopnez a4, .Lbytesetdone +#else /* !XCHAL_HAVE_LOOPS */ + beqz a4, .Lbytesetdone + add a6, a5, a4 # a6 = ending address +#endif /* !XCHAL_HAVE_LOOPS */ +.Lbyteloop: + EX(s8i, a3, a5, 0, memset_fixup) + addi a5, a5, 1 +#if !XCHAL_HAVE_LOOPS + blt a5, a6, .Lbyteloop +#endif /* !XCHAL_HAVE_LOOPS */ +.Lbytesetdone: + retw + + + .section .fixup, "ax" + .align 4 + +/* We return zero if a failure occurred. */ + +memset_fixup: + movi a2, 0 + retw diff --git a/arch/xtensa/lib/pci-auto.c b/arch/xtensa/lib/pci-auto.c new file mode 100644 index 000000000000..90c790f6123b --- /dev/null +++ b/arch/xtensa/lib/pci-auto.c @@ -0,0 +1,352 @@ +/* + * arch/xtensa/kernel/pci-auto.c + * + * PCI autoconfiguration library + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * + * Based on work from Matt Porter + * + * 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 + + +/* + * + * Setting up a PCI + * + * pci_ctrl->first_busno = + * pci_ctrl->last_busno = + * pci_ctrl->ops = + * pci_ctrl->map_irq = + * + * pci_ctrl->io_space.start = + * pci_ctrl->io_space.end = + * pci_ctrl->io_space.base = + * pci_ctrl->mem_space.start = + * pci_ctrl->mem_space.end = + * pci_ctrl->mem_space.base = + * + * pcibios_init_resource(&pci_ctrl->io_resource, , + * , IORESOURCE_IO, "PCI host bridge"); + * pcibios_init_resource(&pci_ctrl->mem_resources[0], , + * , IORESOURCE_MEM, "PCI host bridge"); + * + * pci_ctrl->last_busno = pciauto_bus_scan(pci_ctrl,pci_ctrl->first_busno); + * + * int __init pciauto_bus_scan(struct pci_controller *pci_ctrl, int current_bus) + * + */ + + +/* define DEBUG to print some debugging messages. */ + +#undef DEBUG + +#ifdef DEBUG +# define DBG(x...) printk(x) +#else +# define DBG(x...) +#endif + +static int pciauto_upper_iospc; +static int pciauto_upper_memspc; + +static struct pci_dev pciauto_dev; +static struct pci_bus pciauto_bus; + +/* + * Helper functions + */ + +/* Initialize the bars of a PCI device. */ + +static void __init +pciauto_setup_bars(struct pci_dev *dev, int bar_limit) +{ + int bar_size; + int bar, bar_nr; + int *upper_limit; + int found_mem64 = 0; + + for (bar = PCI_BASE_ADDRESS_0, bar_nr = 0; + bar <= bar_limit; + bar+=4, bar_nr++) + { + /* Tickle the BAR and get the size */ + pci_write_config_dword(dev, bar, 0xffffffff); + pci_read_config_dword(dev, bar, &bar_size); + + /* If BAR is not implemented go to the next BAR */ + if (!bar_size) + continue; + + /* Check the BAR type and set our address mask */ + if (bar_size & PCI_BASE_ADDRESS_SPACE_IO) + { + bar_size &= PCI_BASE_ADDRESS_IO_MASK; + upper_limit = &pciauto_upper_iospc; + DBG("PCI Autoconfig: BAR %d, I/O, ", bar_nr); + } + else + { + if ((bar_size & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + found_mem64 = 1; + + bar_size &= PCI_BASE_ADDRESS_MEM_MASK; + upper_limit = &pciauto_upper_memspc; + DBG("PCI Autoconfig: BAR %d, Mem, ", bar_nr); + } + + /* Allocate a base address (bar_size is negative!) */ + *upper_limit = (*upper_limit + bar_size) & bar_size; + + /* Write it out and update our limit */ + pci_write_config_dword(dev, bar, *upper_limit); + + /* + * If we are a 64-bit decoder then increment to the + * upper 32 bits of the bar and force it to locate + * in the lower 4GB of memory. + */ + + if (found_mem64) + pci_write_config_dword(dev, (bar+=4), 0x00000000); + + DBG("size=0x%x, address=0x%x\n", ~bar_size + 1, *upper_limit); + } +} + +/* Initialize the interrupt number. */ + +static void __init +pciauto_setup_irq(struct pci_controller* pci_ctrl,struct pci_dev *dev,int devfn) +{ + u8 pin; + int irq = 0; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + + /* Fix illegal pin numbers. */ + + if (pin == 0 || pin > 4) + pin = 1; + + if (pci_ctrl->map_irq) + irq = pci_ctrl->map_irq(dev, PCI_SLOT(devfn), pin); + + if (irq == -1) + irq = 0; + + DBG("PCI Autoconfig: Interrupt %d, pin %d\n", irq, pin); + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + + +static void __init +pciauto_prescan_setup_bridge(struct pci_dev *dev, int current_bus, + int sub_bus, int *iosave, int *memsave) +{ + /* Configure bus number registers */ + pci_write_config_byte(dev, PCI_PRIMARY_BUS, current_bus); + pci_write_config_byte(dev, PCI_SECONDARY_BUS, sub_bus + 1); + pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, 0xff); + + /* Round memory allocator to 1MB boundary */ + pciauto_upper_memspc &= ~(0x100000 - 1); + *memsave = pciauto_upper_memspc; + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + *iosave = pciauto_upper_iospc; + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + pci_write_config_word(dev, PCI_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + pci_write_config_byte(dev, PCI_IO_LIMIT, + ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8); + pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16, + ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16); +} + +static void __init +pciauto_postscan_setup_bridge(struct pci_dev *dev, int current_bus, int sub_bus, + int *iosave, int *memsave) +{ + int cmdstat; + + /* Configure bus number registers */ + pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, sub_bus); + + /* + * Round memory allocator to 1MB boundary. + * If no space used, allocate minimum. + */ + pciauto_upper_memspc &= ~(0x100000 - 1); + if (*memsave == pciauto_upper_memspc) + pciauto_upper_memspc -= 0x00100000; + + pci_write_config_word(dev, PCI_MEMORY_BASE, pciauto_upper_memspc >> 16); + + /* Allocate 1MB for pre-fretch */ + pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + + pciauto_upper_memspc -= 0x100000; + + pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, + pciauto_upper_memspc >> 16); + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + if (*iosave == pciauto_upper_iospc) + pciauto_upper_iospc -= 0x1000; + + pci_write_config_byte(dev, PCI_IO_BASE, + (pciauto_upper_iospc & 0x0000f000) >> 8); + pci_write_config_word(dev, PCI_IO_BASE_UPPER16, + pciauto_upper_iospc >> 16); + + /* Enable memory and I/O accesses, enable bus master */ + pci_read_config_dword(dev, PCI_COMMAND, &cmdstat); + pci_write_config_dword(dev, PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); +} + +/* + * Scan the current PCI bus. + */ + + +int __init pciauto_bus_scan(struct pci_controller *pci_ctrl, int current_bus) +{ + int sub_bus, pci_devfn, pci_class, cmdstat, found_multi=0; + unsigned short vid; + unsigned char header_type; + struct pci_dev *dev = &pciauto_dev; + + pciauto_dev.bus = &pciauto_bus; + pciauto_dev.sysdata = pci_ctrl; + pciauto_bus.ops = pci_ctrl->ops; + + /* + * Fetch our I/O and memory space upper boundaries used + * to allocated base addresses on this pci_controller. + */ + + if (current_bus == pci_ctrl->first_busno) + { + pciauto_upper_iospc = pci_ctrl->io_resource.end + 1; + pciauto_upper_memspc = pci_ctrl->mem_resources[0].end + 1; + } + + sub_bus = current_bus; + + for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) + { + /* Skip our host bridge */ + if ((current_bus == pci_ctrl->first_busno) && (pci_devfn == 0)) + continue; + + if (PCI_FUNC(pci_devfn) && !found_multi) + continue; + + pciauto_bus.number = current_bus; + pciauto_dev.devfn = pci_devfn; + + /* If config space read fails from this device, move on */ + if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type)) + continue; + + if (!PCI_FUNC(pci_devfn)) + found_multi = header_type & 0x80; + pci_read_config_word(dev, PCI_VENDOR_ID, &vid); + + if (vid == 0xffff || vid == 0x0000) { + found_multi = 0; + continue; + } + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &pci_class); + + if ((pci_class >> 16) == PCI_CLASS_BRIDGE_PCI) { + + int iosave, memsave; + + DBG("PCI Autoconfig: Found P2P bridge, device %d\n", + PCI_SLOT(pci_devfn)); + + /* Allocate PCI I/O and/or memory space */ + pciauto_setup_bars(dev, PCI_BASE_ADDRESS_1); + + pciauto_prescan_setup_bridge(dev, current_bus, sub_bus, + &iosave, &memsave); + sub_bus = pciauto_bus_scan(pci_ctrl, sub_bus+1); + pciauto_postscan_setup_bridge(dev, current_bus, sub_bus, + &iosave, &memsave); + pciauto_bus.number = current_bus; + + continue; + + } + + +#if 0 + /* Skip legacy mode IDE controller */ + + if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) { + + unsigned char prg_iface; + pci_read_config_byte(dev, PCI_CLASS_PROG, &prg_iface); + + if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { + DBG("PCI Autoconfig: Skipping legacy mode " + "IDE controller\n"); + continue; + } + } +#endif + + /* + * Found a peripheral, enable some standard + * settings + */ + + pci_read_config_dword(dev, PCI_COMMAND, &cmdstat); + pci_write_config_dword(dev, PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x80); + + /* Allocate PCI I/O and/or memory space */ + DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n", + current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) ); + + pciauto_setup_bars(dev, PCI_BASE_ADDRESS_5); + pciauto_setup_irq(pci_ctrl, dev, pci_devfn); + } + return sub_bus; +} + + + + + diff --git a/arch/xtensa/lib/strcasecmp.c b/arch/xtensa/lib/strcasecmp.c new file mode 100644 index 000000000000..165b2d6effa5 --- /dev/null +++ b/arch/xtensa/lib/strcasecmp.c @@ -0,0 +1,32 @@ +/* + * linux/arch/xtensa/lib/strcasecmp.c + * + * 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) 2002 Tensilica Inc. + */ + +#include + + +/* We handle nothing here except the C locale. Since this is used in + only one place, on strings known to contain only 7 bit ASCII, this + is ok. */ + +int strcasecmp(const char *a, const char *b) +{ + int ca, cb; + + do { + ca = *a++ & 0xff; + cb = *b++ & 0xff; + if (ca >= 'A' && ca <= 'Z') + ca += 'a' - 'A'; + if (cb >= 'A' && cb <= 'Z') + cb += 'a' - 'A'; + } while (ca == cb && ca != '\0'); + + return ca - cb; +} diff --git a/arch/xtensa/lib/strncpy_user.S b/arch/xtensa/lib/strncpy_user.S new file mode 100644 index 000000000000..71d55df43893 --- /dev/null +++ b/arch/xtensa/lib/strncpy_user.S @@ -0,0 +1,224 @@ +/* + * arch/xtensa/lib/strncpy_user.S + * + * 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. + * + * Returns: -EFAULT if exception before terminator, N if the entire + * buffer filled, else strlen. + * + * Copyright (C) 2002 Tensilica Inc. + */ + +#include +#include + +/* Load or store instructions that may cause exceptions use the EX macro. */ + +#define EX(insn,reg1,reg2,offset,handler) \ +9: insn reg1, reg2, offset; \ + .section __ex_table, "a"; \ + .word 9b, handler; \ + .previous + +/* + * char *__strncpy_user(char *dst, const char *src, size_t len) + */ +.text +.begin literal +.align 4 +.Lmask0: + .byte 0xff, 0x00, 0x00, 0x00 +.Lmask1: + .byte 0x00, 0xff, 0x00, 0x00 +.Lmask2: + .byte 0x00, 0x00, 0xff, 0x00 +.Lmask3: + .byte 0x00, 0x00, 0x00, 0xff +.end literal + +# Register use +# a0/ return address +# a1/ stack pointer +# a2/ return value +# a3/ src +# a4/ len +# a5/ mask0 +# a6/ mask1 +# a7/ mask2 +# a8/ mask3 +# a9/ tmp +# a10/ tmp +# a11/ dst +# a12/ tmp + +.align 4 +.global __strncpy_user +.type __strncpy_user,@function +__strncpy_user: + entry sp, 16 # minimal stack frame + # a2/ dst, a3/ src, a4/ len + mov a11, a2 # leave dst in return value register + beqz a4, .Lret # if len is zero + l32r a5, .Lmask0 # mask for byte 0 + l32r a6, .Lmask1 # mask for byte 1 + l32r a7, .Lmask2 # mask for byte 2 + l32r a8, .Lmask3 # mask for byte 3 + bbsi.l a3, 0, .Lsrc1mod2 # if only 8-bit aligned + bbsi.l a3, 1, .Lsrc2mod4 # if only 16-bit aligned +.Lsrcaligned: # return here when src is word-aligned + srli a12, a4, 2 # number of loop iterations with 4B per loop + movi a9, 3 + bnone a11, a9, .Laligned + j .Ldstunaligned + +.Lsrc1mod2: # src address is odd + EX(l8ui, a9, a3, 0, fixup_l) # get byte 0 + addi a3, a3, 1 # advance src pointer + EX(s8i, a9, a11, 0, fixup_s) # store byte 0 + beqz a9, .Lret # if byte 0 is zero + addi a11, a11, 1 # advance dst pointer + addi a4, a4, -1 # decrement len + beqz a4, .Lret # if len is zero + bbci.l a3, 1, .Lsrcaligned # if src is now word-aligned + +.Lsrc2mod4: # src address is 2 mod 4 + EX(l8ui, a9, a3, 0, fixup_l) # get byte 0 + /* 1-cycle interlock */ + EX(s8i, a9, a11, 0, fixup_s) # store byte 0 + beqz a9, .Lret # if byte 0 is zero + addi a11, a11, 1 # advance dst pointer + addi a4, a4, -1 # decrement len + beqz a4, .Lret # if len is zero + EX(l8ui, a9, a3, 1, fixup_l) # get byte 0 + addi a3, a3, 2 # advance src pointer + EX(s8i, a9, a11, 0, fixup_s) # store byte 0 + beqz a9, .Lret # if byte 0 is zero + addi a11, a11, 1 # advance dst pointer + addi a4, a4, -1 # decrement len + bnez a4, .Lsrcaligned # if len is nonzero +.Lret: + sub a2, a11, a2 # compute strlen + retw + +/* + * dst is word-aligned, src is word-aligned + */ + .align 4 # 1 mod 4 alignment for LOOPNEZ + .byte 0 # (0 mod 4 alignment for LBEG) +.Laligned: +#if XCHAL_HAVE_LOOPS + loopnez a12, .Loop1done +#else + beqz a12, .Loop1done + slli a12, a12, 2 + add a12, a12, a11 # a12 = end of last 4B chunck +#endif +.Loop1: + EX(l32i, a9, a3, 0, fixup_l) # get word from src + addi a3, a3, 4 # advance src pointer + bnone a9, a5, .Lz0 # if byte 0 is zero + bnone a9, a6, .Lz1 # if byte 1 is zero + bnone a9, a7, .Lz2 # if byte 2 is zero + EX(s32i, a9, a11, 0, fixup_s) # store word to dst + bnone a9, a8, .Lz3 # if byte 3 is zero + addi a11, a11, 4 # advance dst pointer +#if !XCHAL_HAVE_LOOPS + blt a11, a12, .Loop1 +#endif + +.Loop1done: + bbci.l a4, 1, .L100 + # copy 2 bytes + EX(l16ui, a9, a3, 0, fixup_l) + addi a3, a3, 2 # advance src pointer +#ifdef __XTENSA_EB__ + bnone a9, a7, .Lz0 # if byte 2 is zero + bnone a9, a8, .Lz1 # if byte 3 is zero +#else + bnone a9, a5, .Lz0 # if byte 0 is zero + bnone a9, a6, .Lz1 # if byte 1 is zero +#endif + EX(s16i, a9, a11, 0, fixup_s) + addi a11, a11, 2 # advance dst pointer +.L100: + bbci.l a4, 0, .Lret + EX(l8ui, a9, a3, 0, fixup_l) + /* slot */ + EX(s8i, a9, a11, 0, fixup_s) + beqz a9, .Lret # if byte is zero + addi a11, a11, 1-3 # advance dst ptr 1, but also cancel + # the effect of adding 3 in .Lz3 code + /* fall thru to .Lz3 and "retw" */ + +.Lz3: # byte 3 is zero + addi a11, a11, 3 # advance dst pointer + sub a2, a11, a2 # compute strlen + retw +.Lz0: # byte 0 is zero +#ifdef __XTENSA_EB__ + movi a9, 0 +#endif /* __XTENSA_EB__ */ + EX(s8i, a9, a11, 0, fixup_s) + sub a2, a11, a2 # compute strlen + retw +.Lz1: # byte 1 is zero +#ifdef __XTENSA_EB__ + extui a9, a9, 16, 16 +#endif /* __XTENSA_EB__ */ + EX(s16i, a9, a11, 0, fixup_s) + addi a11, a11, 1 # advance dst pointer + sub a2, a11, a2 # compute strlen + retw +.Lz2: # byte 2 is zero +#ifdef __XTENSA_EB__ + extui a9, a9, 16, 16 +#endif /* __XTENSA_EB__ */ + EX(s16i, a9, a11, 0, fixup_s) + movi a9, 0 + EX(s8i, a9, a11, 2, fixup_s) + addi a11, a11, 2 # advance dst pointer + sub a2, a11, a2 # compute strlen + retw + + .align 4 # 1 mod 4 alignment for LOOPNEZ + .byte 0 # (0 mod 4 alignment for LBEG) +.Ldstunaligned: +/* + * for now just use byte copy loop + */ +#if XCHAL_HAVE_LOOPS + loopnez a4, .Lunalignedend +#else + beqz a4, .Lunalignedend + add a12, a11, a4 # a12 = ending address +#endif /* XCHAL_HAVE_LOOPS */ +.Lnextbyte: + EX(l8ui, a9, a3, 0, fixup_l) + addi a3, a3, 1 + EX(s8i, a9, a11, 0, fixup_s) + beqz a9, .Lunalignedend + addi a11, a11, 1 +#if !XCHAL_HAVE_LOOPS + blt a11, a12, .Lnextbyte +#endif + +.Lunalignedend: + sub a2, a11, a2 # compute strlen + retw + + + .section .fixup, "ax" + .align 4 + + /* For now, just return -EFAULT. Future implementations might + * like to clear remaining kernel space, like the fixup + * implementation in memset(). Thus, we differentiate between + * load/store fixups. */ + +fixup_s: +fixup_l: + movi a2, -EFAULT + retw + diff --git a/arch/xtensa/lib/strnlen_user.S b/arch/xtensa/lib/strnlen_user.S new file mode 100644 index 000000000000..cdff4d670f3b --- /dev/null +++ b/arch/xtensa/lib/strnlen_user.S @@ -0,0 +1,147 @@ +/* + * arch/xtensa/lib/strnlen_user.S + * + * 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. + * + * Returns strnlen, including trailing zero terminator. + * Zero indicates error. + * + * Copyright (C) 2002 Tensilica Inc. + */ + +#include + +/* Load or store instructions that may cause exceptions use the EX macro. */ + +#define EX(insn,reg1,reg2,offset,handler) \ +9: insn reg1, reg2, offset; \ + .section __ex_table, "a"; \ + .word 9b, handler; \ + .previous + +/* + * size_t __strnlen_user(const char *s, size_t len) + */ +.text +.begin literal +.align 4 +.Lmask0: + .byte 0xff, 0x00, 0x00, 0x00 +.Lmask1: + .byte 0x00, 0xff, 0x00, 0x00 +.Lmask2: + .byte 0x00, 0x00, 0xff, 0x00 +.Lmask3: + .byte 0x00, 0x00, 0x00, 0xff +.end literal + +# Register use: +# a2/ src +# a3/ len +# a4/ tmp +# a5/ mask0 +# a6/ mask1 +# a7/ mask2 +# a8/ mask3 +# a9/ tmp +# a10/ tmp + +.align 4 +.global __strnlen_user +.type __strnlen_user,@function +__strnlen_user: + entry sp, 16 # minimal stack frame + # a2/ s, a3/ len + addi a4, a2, -4 # because we overincrement at the end; + # we compensate with load offsets of 4 + l32r a5, .Lmask0 # mask for byte 0 + l32r a6, .Lmask1 # mask for byte 1 + l32r a7, .Lmask2 # mask for byte 2 + l32r a8, .Lmask3 # mask for byte 3 + bbsi.l a2, 0, .L1mod2 # if only 8-bit aligned + bbsi.l a2, 1, .L2mod4 # if only 16-bit aligned + +/* + * String is word-aligned. + */ +.Laligned: + srli a10, a3, 2 # number of loop iterations with 4B per loop +#if XCHAL_HAVE_LOOPS + loopnez a10, .Ldone +#else + beqz a10, .Ldone + slli a10, a10, 2 + add a10, a10, a4 # a10 = end of last 4B chunk +#endif /* XCHAL_HAVE_LOOPS */ +.Loop: + EX(l32i, a9, a4, 4, lenfixup) # get next word of string + addi a4, a4, 4 # advance string pointer + bnone a9, a5, .Lz0 # if byte 0 is zero + bnone a9, a6, .Lz1 # if byte 1 is zero + bnone a9, a7, .Lz2 # if byte 2 is zero + bnone a9, a8, .Lz3 # if byte 3 is zero +#if !XCHAL_HAVE_LOOPS + blt a4, a10, .Loop +#endif + +.Ldone: + EX(l32i, a9, a4, 4, lenfixup) # load 4 bytes for remaining checks + + bbci.l a3, 1, .L100 + # check two more bytes (bytes 0, 1 of word) + addi a4, a4, 2 # advance string pointer + bnone a9, a5, .Lz0 # if byte 0 is zero + bnone a9, a6, .Lz1 # if byte 1 is zero +.L100: + bbci.l a3, 0, .L101 + # check one more byte (byte 2 of word) + # Actually, we don't need to check. Zero or nonzero, we'll add one. + # Do not add an extra one for the NULL terminator since we have + # exhausted the original len parameter. + addi a4, a4, 1 # advance string pointer +.L101: + sub a2, a4, a2 # compute length + retw + +# NOTE that in several places below, we point to the byte just after +# the zero byte in order to include the NULL terminator in the count. + +.Lz3: # byte 3 is zero + addi a4, a4, 3 # point to zero byte +.Lz0: # byte 0 is zero + addi a4, a4, 1 # point just beyond zero byte + sub a2, a4, a2 # subtract to get length + retw +.Lz1: # byte 1 is zero + addi a4, a4, 1+1 # point just beyond zero byte + sub a2, a4, a2 # subtract to get length + retw +.Lz2: # byte 2 is zero + addi a4, a4, 2+1 # point just beyond zero byte + sub a2, a4, a2 # subtract to get length + retw + +.L1mod2: # address is odd + EX(l8ui, a9, a4, 4, lenfixup) # get byte 0 + addi a4, a4, 1 # advance string pointer + beqz a9, .Lz3 # if byte 0 is zero + bbci.l a4, 1, .Laligned # if string pointer is now word-aligned + +.L2mod4: # address is 2 mod 4 + addi a4, a4, 2 # advance ptr for aligned access + EX(l32i, a9, a4, 0, lenfixup) # get word with first two bytes of string + bnone a9, a7, .Lz2 # if byte 2 (of word, not string) is zero + bany a9, a8, .Laligned # if byte 3 (of word, not string) is nonzero + # byte 3 is zero + addi a4, a4, 3+1 # point just beyond zero byte + sub a2, a4, a2 # subtract to get length + retw + + .section .fixup, "ax" + .align 4 +lenfixup: + movi a2, 0 + retw + diff --git a/arch/xtensa/lib/usercopy.S b/arch/xtensa/lib/usercopy.S new file mode 100644 index 000000000000..265db2693cbd --- /dev/null +++ b/arch/xtensa/lib/usercopy.S @@ -0,0 +1,321 @@ +/* + * arch/xtensa/lib/usercopy.S + * + * Copy to/from user space (derived from arch/xtensa/lib/hal/memcopy.S) + * + * DO NOT COMBINE this function with . + * It needs to remain separate and distinct. The hal files are part + * of the the Xtensa link-time HAL, and those files may differ per + * processor configuration. Patching the kernel for another + * processor configuration includes replacing the hal files, and we + * could loose the special functionality for accessing user-space + * memory during such a patch. We sacrifice a little code space here + * in favor to simplify code maintenance. + * + * 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) 2002 Tensilica Inc. + */ + + +/* + * size_t __xtensa_copy_user (void *dst, const void *src, size_t len); + * + * The returned value is the number of bytes not copied. Implies zero + * is success. + * + * The general case algorithm is as follows: + * If the destination and source are both aligned, + * do 16B chunks with a loop, and then finish up with + * 8B, 4B, 2B, and 1B copies conditional on the length. + * If destination is aligned and source unaligned, + * do the same, but use SRC to align the source data. + * If destination is unaligned, align it by conditionally + * copying 1B and 2B and then retest. + * This code tries to use fall-through braches for the common + * case of aligned destinations (except for the branches to + * the alignment label). + * + * Register use: + * a0/ return address + * a1/ stack pointer + * a2/ return value + * a3/ src + * a4/ length + * a5/ dst + * a6/ tmp + * a7/ tmp + * a8/ tmp + * a9/ tmp + * a10/ tmp + * a11/ original length + */ + +#include + +#ifdef __XTENSA_EB__ +#define ALIGN(R, W0, W1) src R, W0, W1 +#define SSA8(R) ssa8b R +#else +#define ALIGN(R, W0, W1) src R, W1, W0 +#define SSA8(R) ssa8l R +#endif + +/* Load or store instructions that may cause exceptions use the EX macro. */ + +#define EX(insn,reg1,reg2,offset,handler) \ +9: insn reg1, reg2, offset; \ + .section __ex_table, "a"; \ + .word 9b, handler; \ + .previous + + + .text + .align 4 + .global __xtensa_copy_user + .type __xtensa_copy_user,@function +__xtensa_copy_user: + entry sp, 16 # minimal stack frame + # a2/ dst, a3/ src, a4/ len + mov a5, a2 # copy dst so that a2 is return value + mov a11, a4 # preserve original len for error case +.Lcommon: + bbsi.l a2, 0, .Ldst1mod2 # if dst is 1 mod 2 + bbsi.l a2, 1, .Ldst2mod4 # if dst is 2 mod 4 +.Ldstaligned: # return here from .Ldstunaligned when dst is aligned + srli a7, a4, 4 # number of loop iterations with 16B + # per iteration + movi a8, 3 # if source is also aligned, + bnone a3, a8, .Laligned # then use word copy + SSA8( a3) # set shift amount from byte offset + bnez a4, .Lsrcunaligned + movi a2, 0 # return success for len==0 + retw + +/* + * Destination is unaligned + */ + +.Ldst1mod2: # dst is only byte aligned + bltui a4, 7, .Lbytecopy # do short copies byte by byte + + # copy 1 byte + EX(l8ui, a6, a3, 0, l_fixup) + addi a3, a3, 1 + EX(s8i, a6, a5, 0, s_fixup) + addi a5, a5, 1 + addi a4, a4, -1 + bbci.l a5, 1, .Ldstaligned # if dst is now aligned, then + # return to main algorithm +.Ldst2mod4: # dst 16-bit aligned + # copy 2 bytes + bltui a4, 6, .Lbytecopy # do short copies byte by byte + EX(l8ui, a6, a3, 0, l_fixup) + EX(l8ui, a7, a3, 1, l_fixup) + addi a3, a3, 2 + EX(s8i, a6, a5, 0, s_fixup) + EX(s8i, a7, a5, 1, s_fixup) + addi a5, a5, 2 + addi a4, a4, -2 + j .Ldstaligned # dst is now aligned, return to main algorithm + +/* + * Byte by byte copy + */ + .align 4 + .byte 0 # 1 mod 4 alignment for LOOPNEZ + # (0 mod 4 alignment for LBEG) +.Lbytecopy: +#if XCHAL_HAVE_LOOPS + loopnez a4, .Lbytecopydone +#else /* !XCHAL_HAVE_LOOPS */ + beqz a4, .Lbytecopydone + add a7, a3, a4 # a7 = end address for source +#endif /* !XCHAL_HAVE_LOOPS */ +.Lnextbyte: + EX(l8ui, a6, a3, 0, l_fixup) + addi a3, a3, 1 + EX(s8i, a6, a5, 0, s_fixup) + addi a5, a5, 1 +#if !XCHAL_HAVE_LOOPS + blt a3, a7, .Lnextbyte +#endif /* !XCHAL_HAVE_LOOPS */ +.Lbytecopydone: + movi a2, 0 # return success for len bytes copied + retw + +/* + * Destination and source are word-aligned. + */ + # copy 16 bytes per iteration for word-aligned dst and word-aligned src + .align 4 # 1 mod 4 alignment for LOOPNEZ + .byte 0 # (0 mod 4 alignment for LBEG) +.Laligned: +#if XCHAL_HAVE_LOOPS + loopnez a7, .Loop1done +#else /* !XCHAL_HAVE_LOOPS */ + beqz a7, .Loop1done + slli a8, a7, 4 + add a8, a8, a3 # a8 = end of last 16B source chunk +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop1: + EX(l32i, a6, a3, 0, l_fixup) + EX(l32i, a7, a3, 4, l_fixup) + EX(s32i, a6, a5, 0, s_fixup) + EX(l32i, a6, a3, 8, l_fixup) + EX(s32i, a7, a5, 4, s_fixup) + EX(l32i, a7, a3, 12, l_fixup) + EX(s32i, a6, a5, 8, s_fixup) + addi a3, a3, 16 + EX(s32i, a7, a5, 12, s_fixup) + addi a5, a5, 16 +#if !XCHAL_HAVE_LOOPS + blt a3, a8, .Loop1 +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop1done: + bbci.l a4, 3, .L2 + # copy 8 bytes + EX(l32i, a6, a3, 0, l_fixup) + EX(l32i, a7, a3, 4, l_fixup) + addi a3, a3, 8 + EX(s32i, a6, a5, 0, s_fixup) + EX(s32i, a7, a5, 4, s_fixup) + addi a5, a5, 8 +.L2: + bbci.l a4, 2, .L3 + # copy 4 bytes + EX(l32i, a6, a3, 0, l_fixup) + addi a3, a3, 4 + EX(s32i, a6, a5, 0, s_fixup) + addi a5, a5, 4 +.L3: + bbci.l a4, 1, .L4 + # copy 2 bytes + EX(l16ui, a6, a3, 0, l_fixup) + addi a3, a3, 2 + EX(s16i, a6, a5, 0, s_fixup) + addi a5, a5, 2 +.L4: + bbci.l a4, 0, .L5 + # copy 1 byte + EX(l8ui, a6, a3, 0, l_fixup) + EX(s8i, a6, a5, 0, s_fixup) +.L5: + movi a2, 0 # return success for len bytes copied + retw + +/* + * Destination is aligned, Source is unaligned + */ + + .align 4 + .byte 0 # 1 mod 4 alignement for LOOPNEZ + # (0 mod 4 alignment for LBEG) +.Lsrcunaligned: + # copy 16 bytes per iteration for word-aligned dst and unaligned src + and a10, a3, a8 # save unalignment offset for below + sub a3, a3, a10 # align a3 (to avoid sim warnings only; not needed for hardware) + EX(l32i, a6, a3, 0, l_fixup) # load first word +#if XCHAL_HAVE_LOOPS + loopnez a7, .Loop2done +#else /* !XCHAL_HAVE_LOOPS */ + beqz a7, .Loop2done + slli a10, a7, 4 + add a10, a10, a3 # a10 = end of last 16B source chunk +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop2: + EX(l32i, a7, a3, 4, l_fixup) + EX(l32i, a8, a3, 8, l_fixup) + ALIGN( a6, a6, a7) + EX(s32i, a6, a5, 0, s_fixup) + EX(l32i, a9, a3, 12, l_fixup) + ALIGN( a7, a7, a8) + EX(s32i, a7, a5, 4, s_fixup) + EX(l32i, a6, a3, 16, l_fixup) + ALIGN( a8, a8, a9) + EX(s32i, a8, a5, 8, s_fixup) + addi a3, a3, 16 + ALIGN( a9, a9, a6) + EX(s32i, a9, a5, 12, s_fixup) + addi a5, a5, 16 +#if !XCHAL_HAVE_LOOPS + blt a3, a10, .Loop2 +#endif /* !XCHAL_HAVE_LOOPS */ +.Loop2done: + bbci.l a4, 3, .L12 + # copy 8 bytes + EX(l32i, a7, a3, 4, l_fixup) + EX(l32i, a8, a3, 8, l_fixup) + ALIGN( a6, a6, a7) + EX(s32i, a6, a5, 0, s_fixup) + addi a3, a3, 8 + ALIGN( a7, a7, a8) + EX(s32i, a7, a5, 4, s_fixup) + addi a5, a5, 8 + mov a6, a8 +.L12: + bbci.l a4, 2, .L13 + # copy 4 bytes + EX(l32i, a7, a3, 4, l_fixup) + addi a3, a3, 4 + ALIGN( a6, a6, a7) + EX(s32i, a6, a5, 0, s_fixup) + addi a5, a5, 4 + mov a6, a7 +.L13: + add a3, a3, a10 # readjust a3 with correct misalignment + bbci.l a4, 1, .L14 + # copy 2 bytes + EX(l8ui, a6, a3, 0, l_fixup) + EX(l8ui, a7, a3, 1, l_fixup) + addi a3, a3, 2 + EX(s8i, a6, a5, 0, s_fixup) + EX(s8i, a7, a5, 1, s_fixup) + addi a5, a5, 2 +.L14: + bbci.l a4, 0, .L15 + # copy 1 byte + EX(l8ui, a6, a3, 0, l_fixup) + EX(s8i, a6, a5, 0, s_fixup) +.L15: + movi a2, 0 # return success for len bytes copied + retw + + + .section .fixup, "ax" + .align 4 + +/* a2 = original dst; a5 = current dst; a11= original len + * bytes_copied = a5 - a2 + * retval = bytes_not_copied = original len - bytes_copied + * retval = a11 - (a5 - a2) + * + * Clearing the remaining pieces of kernel memory plugs security + * holes. This functionality is the equivalent of the *_zeroing + * functions that some architectures provide. + */ + +.Lmemset: + .word memset + +s_fixup: + sub a2, a5, a2 /* a2 <-- bytes copied */ + sub a2, a11, a2 /* a2 <-- bytes not copied */ + retw + +l_fixup: + sub a2, a5, a2 /* a2 <-- bytes copied */ + sub a2, a11, a2 /* a2 <-- bytes not copied == return value */ + + /* void *memset(void *s, int c, size_t n); */ + mov a6, a5 /* s */ + movi a7, 0 /* c */ + mov a8, a2 /* n */ + l32r a4, .Lmemset + callx4 a4 + /* Ignore memset return value in a6. */ + /* a2 still contains bytes not copied. */ + retw + diff --git a/arch/xtensa/mm/Makefile b/arch/xtensa/mm/Makefile new file mode 100644 index 000000000000..a5aed5932d7b --- /dev/null +++ b/arch/xtensa/mm/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the Linux/Xtensa-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... + +obj-y := init.o fault.o tlb.o misc.o +obj-m := +obj-n := +obj- := diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c new file mode 100644 index 000000000000..a945a33e85a1 --- /dev/null +++ b/arch/xtensa/mm/fault.c @@ -0,0 +1,241 @@ +// TODO VM_EXEC flag work-around, cache aliasing +/* + * arch/xtensa/mm/fault.c + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * Joe Taylor + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long asid_cache = ASID_FIRST_VERSION; +void bad_page_fault(struct pt_regs*, unsigned long, int); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * Note: does not handle Miss and MultiHit. + */ + +void do_page_fault(struct pt_regs *regs) +{ + struct vm_area_struct * vma; + struct mm_struct *mm = current->mm; + unsigned int exccause = regs->exccause; + unsigned int address = regs->excvaddr; + siginfo_t info; + + int is_write, is_exec; + + info.si_code = SEGV_MAPERR; + + /* We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + */ + if (address >= TASK_SIZE && !user_mode(regs)) + goto vmalloc_fault; + + /* If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_atomic() || !mm) { + bad_page_fault(regs, address, SIGSEGV); + return; + } + + is_write = (exccause == XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE) ? 1 : 0; + is_exec = (exccause == XCHAL_EXCCAUSE_ITLB_PRIVILEGE || + exccause == XCHAL_EXCCAUSE_ITLB_MISS || + exccause == XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE) ? 1 : 0; + +#if 0 + printk("[%s:%d:%08x:%d:%08x:%s%s]\n", current->comm, current->pid, + address, exccause, regs->pc, is_write? "w":"", is_exec? "x":""); +#endif + + 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 (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; + + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else if (is_exec) { + if (!(vma->vm_flags & VM_EXEC)) + goto bad_area; + } else /* Allow read even from write-only pages. */ + if (!(vma->vm_flags & (VM_READ | VM_WRITE))) + 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. + */ +survive: + switch (handle_mm_fault(mm, vma, address, is_write)) { + case VM_FAULT_MINOR: + current->min_flt++; + break; + case VM_FAULT_MAJOR: + current->maj_flt++; + break; + case VM_FAULT_SIGBUS: + goto do_sigbus; + case VM_FAULT_OOM: + goto out_of_memory; + default: + BUG(); + } + + 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); + if (user_mode(regs)) { + current->thread.bad_vaddr = address; + current->thread.error_code = is_write; + 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, current); + return; + } + bad_page_fault(regs, address, SIGSEGV); + 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); + if (current->pid == 1) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + bad_page_fault(regs, address, SIGKILL); + return; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + current->thread.bad_vaddr = address; + info.si_code = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *) address; + force_sig_info(SIGBUS, &info, current); + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + bad_page_fault(regs, address, SIGBUS); + +vmalloc_fault: + { + /* Synchronize this task's top level page-table + * with the 'reference' page table. + */ + struct mm_struct *act_mm = current->active_mm; + int index = pgd_index(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + + if (act_mm == NULL) + goto bad_page_fault; + + pgd = act_mm->pgd + index; + pgd_k = init_mm.pgd + index; + + if (!pgd_present(*pgd_k)) + goto bad_page_fault; + + pgd_val(*pgd) = pgd_val(*pgd_k); + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + if (!pmd_present(*pmd) || !pmd_present(*pmd_k)) + goto bad_page_fault; + + pmd_val(*pmd) = pmd_val(*pmd_k); + pte_k = pte_offset_kernel(pmd_k, address); + + if (!pte_present(*pte_k)) + goto bad_page_fault; + return; + } +bad_page_fault: + bad_page_fault(regs, address, SIGKILL); + return; +} + + +void +bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) +{ + extern void die(const char*, struct pt_regs*, long); + const struct exception_table_entry *entry; + + /* Are we prepared to handle this kernel fault? */ + if ((entry = search_exception_tables(regs->pc)) != NULL) { +#if 1 + printk(KERN_DEBUG "%s: Exception at pc=%#010lx (%lx)\n", + current->comm, regs->pc, entry->fixup); +#endif + current->thread.bad_uaddr = address; + regs->pc = entry->fixup; + return; + } + + /* Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + printk(KERN_ALERT "Unable to handle kernel paging request at virtual " + "address %08lx\n pc = %08lx, ra = %08lx\n", + address, regs->pc, regs->areg[0]); + die("Oops", regs, sig); + do_exit(sig); +} + diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c new file mode 100644 index 000000000000..56aace84aaeb --- /dev/null +++ b/arch/xtensa/mm/init.c @@ -0,0 +1,551 @@ +/* + * arch/xtensa/mm/init.c + * + * Derived from MIPS, PPC. + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + * Joe Taylor + * Marc Gauthier + * Kevin Chea + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEBUG 0 + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); +//static DEFINE_SPINLOCK(tlb_lock); + +/* + * This flag is used to indicate that the page was mapped and modified in + * kernel space, so the cache is probably dirty at that address. + * If cache aliasing is enabled and the page color mismatches, update_mmu_cache + * synchronizes the caches if this bit is set. + */ + +#define PG_cache_clean PG_arch_1 + +/* References to section boundaries */ + +extern char _ftext, _etext, _fdata, _edata, _rodata_end; +extern char __init_begin, __init_end; + +/* + * mem_reserve(start, end, must_exist) + * + * Reserve some memory from the memory pool. + * + * Parameters: + * start Start of region, + * end End of region, + * must_exist Must exist in memory pool. + * + * Returns: + * 0 (memory area couldn't be mapped) + * -1 (success) + */ + +int __init mem_reserve(unsigned long start, unsigned long end, int must_exist) +{ + int i; + + if (start == end) + return 0; + + start = start & PAGE_MASK; + end = PAGE_ALIGN(end); + + for (i = 0; i < sysmem.nr_banks; i++) + if (start < sysmem.bank[i].end + && end >= sysmem.bank[i].start) + break; + + if (i == sysmem.nr_banks) { + if (must_exist) + printk (KERN_WARNING "mem_reserve: [0x%0lx, 0x%0lx) " + "not in any region!\n", start, end); + return 0; + } + + if (start > sysmem.bank[i].start) { + if (end < sysmem.bank[i].end) { + /* split entry */ + if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) + panic("meminfo overflow\n"); + sysmem.bank[sysmem.nr_banks].start = end; + sysmem.bank[sysmem.nr_banks].end = sysmem.bank[i].end; + sysmem.nr_banks++; + } + sysmem.bank[i].end = start; + } else { + if (end < sysmem.bank[i].end) + sysmem.bank[i].start = end; + else { + /* remove entry */ + sysmem.nr_banks--; + sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start; + sysmem.bank[i].end = sysmem.bank[sysmem.nr_banks].end; + } + } + return -1; +} + + +/* + * Initialize the bootmem system and give it all the memory we have available. + */ + +void __init bootmem_init(void) +{ + unsigned long pfn; + unsigned long bootmap_start, bootmap_size; + int i; + + max_low_pfn = max_pfn = 0; + min_low_pfn = ~0; + + for (i=0; i < sysmem.nr_banks; i++) { + pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT; + if (pfn < min_low_pfn) + min_low_pfn = pfn; + pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT; + if (pfn > max_pfn) + max_pfn = pfn; + } + + if (min_low_pfn > max_pfn) + panic("No memory found!\n"); + + max_low_pfn = max_pfn < MAX_LOW_MEMORY >> PAGE_SHIFT ? + max_pfn : MAX_LOW_MEMORY >> PAGE_SHIFT; + + /* Find an area to use for the bootmem bitmap. */ + + bootmap_size = bootmem_bootmap_pages(max_low_pfn) << PAGE_SHIFT; + bootmap_start = ~0; + + for (i=0; i= bootmap_size) { + bootmap_start = sysmem.bank[i].start; + break; + } + + if (bootmap_start == ~0UL) + panic("Cannot find %ld bytes for bootmap\n", bootmap_size); + + /* Reserve the bootmem bitmap area */ + + mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1); + bootmap_size = init_bootmem_node(NODE_DATA(0), min_low_pfn, + bootmap_start >> PAGE_SHIFT, + max_low_pfn); + + /* Add all remaining memory pieces into the bootmem map */ + + for (i=0; iTLBCFG special registers ensure + * that valid values exist in the register. For existing + * PGSZID fields, zero selects the first element of the + * page-size array. For nonexistant PGSZID fields, zero is + * the best value to write. Also, when changing PGSZID + * fields, the corresponding TLB must be flushed. + */ + set_itlbcfg_register (0); + set_dtlbcfg_register (0); + flush_tlb_all (); + + /* Set rasid register to a known value. */ + + set_rasid_register (ASID_ALL_RESERVED); + + /* Set PTEVADDR special register to the start of the page + * table, which is in kernel mappable space (ie. not + * statically mapped). This register's value is undefined on + * reset. + */ + set_ptevaddr_register (PGTABLE_START); +} + +/* + * Initialize memory pages. + */ + +void __init mem_init(void) +{ + unsigned long codesize, reservedpages, datasize, initsize; + unsigned long highmemsize, tmp, ram; + + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_mapnr << PAGE_SHIFT); + highmemsize = 0; + +#if CONFIG_HIGHMEM +#error HIGHGMEM not implemented in init.c +#endif + + totalram_pages += free_all_bootmem(); + + reservedpages = ram = 0; + for (tmp = 0; tmp < max_low_pfn; tmp++) { + ram++; + if (PageReserved(mem_map+tmp)) + reservedpages++; + } + + codesize = (unsigned long) &_etext - (unsigned long) &_ftext; + datasize = (unsigned long) &_edata - (unsigned long) &_fdata; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, " + "%ldk data, %ldk init %ldk highmem)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + ram << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10, + highmemsize >> 10); +} + +void +free_reserved_mem(void *start, void *end) +{ + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page((unsigned long)start); + totalram_pages++; + } +} + +#ifdef CONFIG_BLK_DEV_INITRD +extern int initrd_is_mapped; + +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (initrd_is_mapped) { + free_reserved_mem((void*)start, (void*)end); + printk ("Freeing initrd memory: %ldk freed\n",(end-start)>>10); + } +} +#endif + +void free_initmem(void) +{ + free_reserved_mem(&__init_begin, &__init_end); + printk("Freeing unused kernel memory: %dk freed\n", + (&__init_end - &__init_begin) >> 10); +} + +void show_mem(void) +{ + int i, free = 0, total = 0, reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6ldkB\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)) + free++; + else + 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("%d free pages\n", free); +} + +/* ------------------------------------------------------------------------- */ + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) + +/* + * With cache aliasing, the page color of the page in kernel space and user + * space might mismatch. We temporarily map the page to a different virtual + * address with the same color and clear the page there. + */ + +void clear_user_page(void *kaddr, unsigned long vaddr, struct page* page) +{ + + /* There shouldn't be any entries for this page. */ + + __flush_invalidate_dcache_page_phys(__pa(page_address(page))); + + if (!PAGE_COLOR_EQ(vaddr, kaddr)) { + unsigned long v, p; + + /* Temporarily map page to DTLB_WAY_DCACHE_ALIAS0. */ + + spin_lock(&tlb_lock); + + p = (unsigned long)pte_val((mk_pte(page,PAGE_KERNEL))); + kaddr = (void*)PAGE_COLOR_MAP0(vaddr); + v = (unsigned long)kaddr | DTLB_WAY_DCACHE_ALIAS0; + __asm__ __volatile__("wdtlb %0,%1; dsync" : :"a" (p), "a" (v)); + + clear_page(kaddr); + + spin_unlock(&tlb_lock); + } else { + clear_page(kaddr); + } + + /* We need to make sure that i$ and d$ are coherent. */ + + clear_bit(PG_cache_clean, &page->flags); +} + +/* + * With cache aliasing, we have to make sure that the page color of the page + * in kernel space matches that of the virtual user address before we read + * the page. If the page color differ, we create a temporary DTLB entry with + * the corrent page color and use this 'temporary' address as the source. + * We then use the same approach as in clear_user_page and copy the data + * to the kernel space and clear the PG_cache_clean bit to synchronize caches + * later. + * + * Note: + * Instead of using another 'way' for the temporary DTLB entry, we could + * probably use the same entry that points to the kernel address (after + * saving the original value and restoring it when we are done). + */ + +void copy_user_page(void* to, void* from, unsigned long vaddr, + struct page* to_page) +{ + /* There shouldn't be any entries for the new page. */ + + __flush_invalidate_dcache_page_phys(__pa(page_address(to_page))); + + spin_lock(&tlb_lock); + + if (!PAGE_COLOR_EQ(vaddr, from)) { + unsigned long v, p, t; + + __asm__ __volatile__ ("pdtlb %1,%2; rdtlb1 %0,%1" + : "=a"(p), "=a"(t) : "a"(from)); + from = (void*)PAGE_COLOR_MAP0(vaddr); + v = (unsigned long)from | DTLB_WAY_DCACHE_ALIAS0; + __asm__ __volatile__ ("wdtlb %0,%1; dsync" ::"a" (p), "a" (v)); + } + + if (!PAGE_COLOR_EQ(vaddr, to)) { + unsigned long v, p; + + p = (unsigned long)pte_val((mk_pte(to_page,PAGE_KERNEL))); + to = (void*)PAGE_COLOR_MAP1(vaddr); + v = (unsigned long)to | DTLB_WAY_DCACHE_ALIAS1; + __asm__ __volatile__ ("wdtlb %0,%1; dsync" ::"a" (p), "a" (v)); + } + copy_page(to, from); + + spin_unlock(&tlb_lock); + + /* We need to make sure that i$ and d$ are coherent. */ + + clear_bit(PG_cache_clean, &to_page->flags); +} + + + +/* + * Any time the kernel writes to a user page cache page, or it is about to + * read from a page cache page this routine is called. + * + * Note: + * The kernel currently only provides one architecture bit in the page + * flags that we use for I$/D$ coherency. Maybe, in future, we can + * use a sepearte bit for deferred dcache aliasing: + * If the page is not mapped yet, we only need to set a flag, + * if mapped, we need to invalidate the page. + */ +// FIXME: we probably need this for WB caches not only for Page Coloring.. + +void flush_dcache_page(struct page *page) +{ + unsigned long addr = __pa(page_address(page)); + struct address_space *mapping = page_mapping(page); + + __flush_invalidate_dcache_page_phys(addr); + + if (!test_bit(PG_cache_clean, &page->flags)) + return; + + /* If this page hasn't been mapped, yet, handle I$/D$ coherency later.*/ +#if 0 + if (mapping && !mapping_mapped(mapping)) + clear_bit(PG_cache_clean, &page->flags); + else +#endif + __invalidate_icache_page_phys(addr); +} + +void flush_cache_range(struct vm_area_struct* vma, unsigned long s, + unsigned long e) +{ + __flush_invalidate_cache_all(); +} + +void flush_cache_page(struct vm_area_struct* vma, unsigned long address, + unsigned long pfn) +{ + struct page *page = pfn_to_page(pfn); + + /* Remove any entry for the old mapping. */ + + if (current->active_mm == vma->vm_mm) { + unsigned long addr = __pa(page_address(page)); + __flush_invalidate_dcache_page_phys(addr); + if ((vma->vm_flags & VM_EXEC) != 0) + __invalidate_icache_page_phys(addr); + } else { + BUG(); + } +} + +#endif /* (DCACHE_WAY_SIZE > PAGE_SIZE) */ + + +pte_t* pte_alloc_one_kernel (struct mm_struct* mm, unsigned long addr) +{ + pte_t* pte = (pte_t*)__get_free_pages(GFP_KERNEL|__GFP_REPEAT, 0); + if (likely(pte)) { + pte_t* ptep = (pte_t*)(pte_val(*pte) + PAGE_OFFSET); + int i; + for (i = 0; i < 1024; i++, ptep++) + pte_clear(mm, addr, ptep); + } + return pte; +} + +struct page* pte_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + struct page *page; + + page = alloc_pages(GFP_KERNEL | __GFP_REPEAT, 0); + + if (likely(page)) { + pte_t* ptep = kmap_atomic(page, KM_USER0); + int i; + + for (i = 0; i < 1024; i++, ptep++) + pte_clear(mm, addr, ptep); + + kunmap_atomic(ptep, KM_USER0); + } + return page; +} + + +/* + * Handle D$/I$ coherency. + * + * Note: + * We only have one architecture bit for the page flags, so we cannot handle + * cache aliasing, yet. + */ + +void +update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t pte) +{ + unsigned long pfn = pte_pfn(pte); + struct page *page; + unsigned long vaddr = addr & PAGE_MASK; + + if (!pfn_valid(pfn)) + return; + + page = pfn_to_page(pfn); + + invalidate_itlb_mapping(addr); + invalidate_dtlb_mapping(addr); + + /* We have a new mapping. Use it. */ + + write_dtlb_entry(pte, dtlb_probe(addr)); + + /* If the processor can execute from this page, synchronize D$/I$. */ + + if ((vma->vm_flags & VM_EXEC) != 0) { + + write_itlb_entry(pte, itlb_probe(addr)); + + /* Synchronize caches, if not clean. */ + + if (!test_and_set_bit(PG_cache_clean, &page->flags)) { + __flush_dcache_page(vaddr); + __invalidate_icache_page(vaddr); + } + } +} + diff --git a/arch/xtensa/mm/misc.S b/arch/xtensa/mm/misc.S new file mode 100644 index 000000000000..327c0f17187c --- /dev/null +++ b/arch/xtensa/mm/misc.S @@ -0,0 +1,374 @@ +/* + * arch/xtensa/mm/misc.S + * + * Miscellaneous assembly functions. + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + */ + +/* Note: we might want to implement some of the loops as zero-overhead-loops, + * where applicable and if supported by the processor. + */ + +#include +#include +#include + +#include +#include + +/* clear_page (page) */ + +ENTRY(clear_page) + entry a1, 16 + addi a4, a2, PAGE_SIZE + movi a3, 0 + +1: s32i a3, a2, 0 + s32i a3, a2, 4 + s32i a3, a2, 8 + s32i a3, a2, 12 + s32i a3, a2, 16 + s32i a3, a2, 20 + s32i a3, a2, 24 + s32i a3, a2, 28 + addi a2, a2, 32 + blt a2, a4, 1b + + retw + +/* + * copy_page (void *to, void *from) + * a2 a3 + */ + +ENTRY(copy_page) + entry a1, 16 + addi a4, a2, PAGE_SIZE + +1: l32i a5, a3, 0 + l32i a6, a3, 4 + l32i a7, a3, 8 + s32i a5, a2, 0 + s32i a6, a2, 4 + s32i a7, a2, 8 + l32i a5, a3, 12 + l32i a6, a3, 16 + l32i a7, a3, 20 + s32i a5, a2, 12 + s32i a6, a2, 16 + s32i a7, a2, 20 + l32i a5, a3, 24 + l32i a6, a3, 28 + s32i a5, a2, 24 + s32i a6, a2, 28 + addi a2, a2, 32 + addi a3, a3, 32 + blt a2, a4, 1b + + retw + + +/* + * void __flush_invalidate_cache_all(void) + */ + +ENTRY(__flush_invalidate_cache_all) + entry sp, 16 + dcache_writeback_inv_all a2, a3 + icache_invalidate_all a2, a3 + retw + +/* + * void __invalidate_icache_all(void) + */ + +ENTRY(__invalidate_icache_all) + entry sp, 16 + icache_invalidate_all a2, a3 + retw + +/* + * void __flush_invalidate_dcache_all(void) + */ + +ENTRY(__flush_invalidate_dcache_all) + entry sp, 16 + dcache_writeback_inv_all a2, a3 + retw + + +/* + * void __flush_invalidate_cache_range(ulong start, ulong size) + */ + +ENTRY(__flush_invalidate_cache_range) + entry sp, 16 + mov a4, a2 + mov a5, a3 + dcache_writeback_inv_region a4, a5, a6 + icache_invalidate_region a2, a3, a4 + retw + +/* + * void __invalidate_icache_page(ulong start) + */ + +ENTRY(__invalidate_icache_page) + entry sp, 16 + movi a3, PAGE_SIZE + icache_invalidate_region a2, a3, a4 + retw + +/* + * void __invalidate_dcache_page(ulong start) + */ + +ENTRY(__invalidate_dcache_page) + entry sp, 16 + movi a3, PAGE_SIZE + dcache_invalidate_region a2, a3, a4 + retw + +/* + * void __invalidate_icache_range(ulong start, ulong size) + */ + +ENTRY(__invalidate_icache_range) + entry sp, 16 + icache_invalidate_region a2, a3, a4 + retw + +/* + * void __invalidate_dcache_range(ulong start, ulong size) + */ + +ENTRY(__invalidate_dcache_range) + entry sp, 16 + dcache_invalidate_region a2, a3, a4 + retw + +/* + * void __flush_dcache_page(ulong start) + */ + +ENTRY(__flush_dcache_page) + entry sp, 16 + movi a3, PAGE_SIZE + dcache_writeback_region a2, a3, a4 + retw + +/* + * void __flush_invalidate_dcache_page(ulong start) + */ + +ENTRY(__flush_invalidate_dcache_page) + entry sp, 16 + movi a3, PAGE_SIZE + dcache_writeback_inv_region a2, a3, a4 + retw + +/* + * void __flush_invalidate_dcache_range(ulong start, ulong size) + */ + +ENTRY(__flush_invalidate_dcache_range) + entry sp, 16 + dcache_writeback_inv_region a2, a3, a4 + retw + +/* + * void __invalidate_dcache_all(void) + */ + +ENTRY(__invalidate_dcache_all) + entry sp, 16 + dcache_invalidate_all a2, a3 + retw + +/* + * void __flush_invalidate_dcache_page_phys(ulong start) + */ + +ENTRY(__flush_invalidate_dcache_page_phys) + entry sp, 16 + + movi a3, XCHAL_DCACHE_SIZE + movi a4, PAGE_MASK | 1 + addi a2, a2, 1 + +1: addi a3, a3, -XCHAL_DCACHE_LINESIZE + + ldct a6, a3 + dsync + and a6, a6, a4 + beq a6, a2, 2f + bgeui a3, 2, 1b + retw + +2: diwbi a3, 0 + bgeui a3, 2, 1b + retw + +ENTRY(check_dcache_low0) + entry sp, 16 + + movi a3, XCHAL_DCACHE_SIZE / 4 + movi a4, PAGE_MASK | 1 + addi a2, a2, 1 + +1: addi a3, a3, -XCHAL_DCACHE_LINESIZE + + ldct a6, a3 + dsync + and a6, a6, a4 + beq a6, a2, 2f + bgeui a3, 2, 1b + retw + +2: j 2b + +ENTRY(check_dcache_high0) + entry sp, 16 + + movi a5, XCHAL_DCACHE_SIZE / 4 + movi a3, XCHAL_DCACHE_SIZE / 2 + movi a4, PAGE_MASK | 1 + addi a2, a2, 1 + +1: addi a3, a3, -XCHAL_DCACHE_LINESIZE + addi a5, a5, -XCHAL_DCACHE_LINESIZE + + ldct a6, a3 + dsync + and a6, a6, a4 + beq a6, a2, 2f + bgeui a5, 2, 1b + retw + +2: j 2b + +ENTRY(check_dcache_low1) + entry sp, 16 + + movi a5, XCHAL_DCACHE_SIZE / 4 + movi a3, XCHAL_DCACHE_SIZE * 3 / 4 + movi a4, PAGE_MASK | 1 + addi a2, a2, 1 + +1: addi a3, a3, -XCHAL_DCACHE_LINESIZE + addi a5, a5, -XCHAL_DCACHE_LINESIZE + + ldct a6, a3 + dsync + and a6, a6, a4 + beq a6, a2, 2f + bgeui a5, 2, 1b + retw + +2: j 2b + +ENTRY(check_dcache_high1) + entry sp, 16 + + movi a5, XCHAL_DCACHE_SIZE / 4 + movi a3, XCHAL_DCACHE_SIZE + movi a4, PAGE_MASK | 1 + addi a2, a2, 1 + +1: addi a3, a3, -XCHAL_DCACHE_LINESIZE + addi a5, a5, -XCHAL_DCACHE_LINESIZE + + ldct a6, a3 + dsync + and a6, a6, a4 + beq a6, a2, 2f + bgeui a5, 2, 1b + retw + +2: j 2b + + +/* + * void __invalidate_icache_page_phys(ulong start) + */ + +ENTRY(__invalidate_icache_page_phys) + entry sp, 16 + + movi a3, XCHAL_ICACHE_SIZE + movi a4, PAGE_MASK | 1 + addi a2, a2, 1 + +1: addi a3, a3, -XCHAL_ICACHE_LINESIZE + + lict a6, a3 + isync + and a6, a6, a4 + beq a6, a2, 2f + bgeui a3, 2, 1b + retw + +2: iii a3, 0 + bgeui a3, 2, 1b + retw + + +#if 0 + + movi a3, XCHAL_DCACHE_WAYS - 1 + movi a4, PAGE_SIZE + +1: mov a5, a2 + add a6, a2, a4 + +2: diwbi a5, 0 + diwbi a5, XCHAL_DCACHE_LINESIZE + diwbi a5, XCHAL_DCACHE_LINESIZE * 2 + diwbi a5, XCHAL_DCACHE_LINESIZE * 3 + + addi a5, a5, XCHAL_DCACHE_LINESIZE * 4 + blt a5, a6, 2b + + addi a3, a3, -1 + addi a2, a2, XCHAL_DCACHE_SIZE / XCHAL_DCACHE_WAYS + bgez a3, 1b + + retw + +ENTRY(__invalidate_icache_page_index) + entry sp, 16 + + movi a3, XCHAL_ICACHE_WAYS - 1 + movi a4, PAGE_SIZE + +1: mov a5, a2 + add a6, a2, a4 + +2: iii a5, 0 + iii a5, XCHAL_ICACHE_LINESIZE + iii a5, XCHAL_ICACHE_LINESIZE * 2 + iii a5, XCHAL_ICACHE_LINESIZE * 3 + + addi a5, a5, XCHAL_ICACHE_LINESIZE * 4 + blt a5, a6, 2b + + addi a3, a3, -1 + addi a2, a2, XCHAL_ICACHE_SIZE / XCHAL_ICACHE_WAYS + bgez a3, 2b + + retw + +#endif + + + + + + diff --git a/arch/xtensa/mm/pgtable.c b/arch/xtensa/mm/pgtable.c new file mode 100644 index 000000000000..e5e119c820e4 --- /dev/null +++ b/arch/xtensa/mm/pgtable.c @@ -0,0 +1,76 @@ +/* + * arch/xtensa/mm/fault.c + * + * 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) 2001 - 2005 Tensilica Inc. + * + * Chris Zankel + */ + +#if (DCACHE_SIZE > PAGE_SIZE) + +pte_t* pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ + pte_t *pte, p; + int color = ADDR_COLOR(address); + int i; + + p = (pte_t*) __get_free_pages(GFP_KERNEL|__GFP_REPEAT, COLOR_ORDER); + + if (likely(p)) { + struct page *page; + + for (i = 0; i < COLOR_SIZE; i++, p++) { + page = virt_to_page(pte); + + set_page_count(page, 1); + ClearPageCompound(page); + + if (ADDR_COLOR(p) == color) + pte = p; + else + free_page(p); + } + clear_page(pte); + } + return pte; +} + +#ifdef PROFILING + +int mask; +int hit; +int flush; + +#endif + +struct page* pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct page *page, p; + int color = ADDR_COLOR(address); + + p = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER); + + if (likely(p)) { + for (i = 0; i < PAGE_ORDER; i++) { + set_page_count(p, 1); + ClearPageCompound(p); + + if (PADDR_COLOR(page_address(pg)) == color) + page = p; + else + free_page(p); + } + clear_highpage(page); + } + + return page; +} + +#endif + + + diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c new file mode 100644 index 000000000000..d3bd3bfc3b3b --- /dev/null +++ b/arch/xtensa/mm/tlb.c @@ -0,0 +1,545 @@ +/* + * arch/xtensa/mm/mmu.c + * + * Logic that manipulates the Xtensa MMU. Derived from MIPS. + * + * 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) 2001 - 2003 Tensilica Inc. + * + * Joe Taylor + * Chris Zankel + * Marc Gauthier + */ + +#include +#include +#include +#include +#include +#include + + +static inline void __flush_itlb_all (void) +{ + int way, index; + + for (way = 0; way < XCHAL_ITLB_ARF_WAYS; way++) { + for (index = 0; index < ITLB_ENTRIES_PER_ARF_WAY; index++) { + int entry = way + (index << PAGE_SHIFT); + invalidate_itlb_entry_no_isync (entry); + } + } + asm volatile ("isync\n"); +} + +static inline void __flush_dtlb_all (void) +{ + int way, index; + + for (way = 0; way < XCHAL_DTLB_ARF_WAYS; way++) { + for (index = 0; index < DTLB_ENTRIES_PER_ARF_WAY; index++) { + int entry = way + (index << PAGE_SHIFT); + invalidate_dtlb_entry_no_isync (entry); + } + } + asm volatile ("isync\n"); +} + + +void flush_tlb_all (void) +{ + __flush_itlb_all(); + __flush_dtlb_all(); +} + +/* If mm is current, we simply assign the current task a new ASID, thus, + * invalidating all previous tlb entries. If mm is someone else's user mapping, + * wie invalidate the context, thus, when that user mapping is swapped in, + * a new context will be assigned to it. + */ + +void flush_tlb_mm(struct mm_struct *mm) +{ +#if 0 + printk("[tlbmm<%lx>]\n", (unsigned long)mm->context); +#endif + + if (mm == current->active_mm) { + int flags; + local_save_flags(flags); + get_new_mmu_context(mm, asid_cache); + set_rasid_register(ASID_INSERT(mm->context)); + local_irq_restore(flags); + } + else + mm->context = 0; +} + +void flush_tlb_range (struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long flags; + + if (mm->context == NO_CONTEXT) + return; + +#if 0 + printk("[tlbrange<%02lx,%08lx,%08lx>]\n", + (unsigned long)mm->context, start, end); +#endif + local_save_flags(flags); + + if (end-start + (PAGE_SIZE-1) <= SMALLEST_NTLB_ENTRIES << PAGE_SHIFT) { + int oldpid = get_rasid_register(); + set_rasid_register (ASID_INSERT(mm->context)); + start &= PAGE_MASK; + if (vma->vm_flags & VM_EXEC) + while(start < end) { + invalidate_itlb_mapping(start); + invalidate_dtlb_mapping(start); + start += PAGE_SIZE; + } + else + while(start < end) { + invalidate_dtlb_mapping(start); + start += PAGE_SIZE; + } + + set_rasid_register(oldpid); + } else { + get_new_mmu_context(mm, asid_cache); + if (mm == current->active_mm) + set_rasid_register(ASID_INSERT(mm->context)); + } + local_irq_restore(flags); +} + +void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) +{ + struct mm_struct* mm = vma->vm_mm; + unsigned long flags; + int oldpid; +#if 0 + printk("[tlbpage<%02lx,%08lx>]\n", + (unsigned long)mm->context, page); +#endif + + if(mm->context == NO_CONTEXT) + return; + + local_save_flags(flags); + + oldpid = get_rasid_register(); + + if (vma->vm_flags & VM_EXEC) + invalidate_itlb_mapping(page); + invalidate_dtlb_mapping(page); + + set_rasid_register(oldpid); + + local_irq_restore(flags); + +#if 0 + flush_tlb_all(); + return; +#endif +} + + +#ifdef DEBUG_TLB + +#define USE_ITLB 0 +#define USE_DTLB 1 + +struct way_config_t { + int indicies; + int indicies_log2; + int pgsz_log2; + int arf; +}; + +static struct way_config_t itlb[XCHAL_ITLB_WAYS] = +{ + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ARF) + }, + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ARF) + }, + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ARF) + }, + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ARF) + }, + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ARF) + }, + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ARF) + }, + { XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES_LOG2), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, PAGESZ_LOG2_MIN), + XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ARF) + } +}; + +static struct way_config_t dtlb[XCHAL_DTLB_WAYS] = +{ + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ARF) + }, + { XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES_LOG2), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, PAGESZ_LOG2_MIN), + XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ARF) + } +}; + +/* Total number of entries: */ +#define ITLB_TOTAL_ENTRIES \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES) + \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES) + \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES) + \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES) + \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES) + \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES) + \ + XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES) +#define DTLB_TOTAL_ENTRIES \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES) + \ + XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES) + + +typedef struct { + unsigned va; + unsigned pa; + unsigned char asid; + unsigned char ca; + unsigned char way; + unsigned char index; + unsigned char pgsz_log2; /* 0 .. 32 */ + unsigned char type; /* 0=ITLB 1=DTLB */ +} tlb_dump_entry_t; + +/* Return -1 if a precedes b, +1 if a follows b, 0 if same: */ +int cmp_tlb_dump_info( tlb_dump_entry_t *a, tlb_dump_entry_t *b ) +{ + if (a->asid < b->asid) return -1; + if (a->asid > b->asid) return 1; + if (a->va < b->va) return -1; + if (a->va > b->va) return 1; + if (a->pa < b->pa) return -1; + if (a->pa > b->pa) return 1; + if (a->ca < b->ca) return -1; + if (a->ca > b->ca) return 1; + if (a->way < b->way) return -1; + if (a->way > b->way) return 1; + if (a->index < b->index) return -1; + if (a->index > b->index) return 1; + return 0; +} + +void sort_tlb_dump_info( tlb_dump_entry_t *t, int n ) +{ + int i, j; + /* Simple O(n*n) sort: */ + for (i = 0; i < n-1; i++) + for (j = i+1; j < n; j++) + if (cmp_tlb_dump_info(t+i, t+j) > 0) { + tlb_dump_entry_t tmp = t[i]; + t[i] = t[j]; + t[j] = tmp; + } +} + + +static tlb_dump_entry_t itlb_dump_info[ITLB_TOTAL_ENTRIES]; +static tlb_dump_entry_t dtlb_dump_info[DTLB_TOTAL_ENTRIES]; + + +static inline char *way_type (int type) +{ + return type ? "autorefill" : "non-autorefill"; +} + +void print_entry (struct way_config_t *way_info, + unsigned int way, + unsigned int index, + unsigned int virtual, + unsigned int translation) +{ + char valid_chr; + unsigned int va, pa, asid, ca; + + va = virtual & + ~((1 << (way_info->pgsz_log2 + way_info->indicies_log2)) - 1); + asid = virtual & ((1 << XCHAL_MMU_ASID_BITS) - 1); + pa = translation & ~((1 << way_info->pgsz_log2) - 1); + ca = translation & ((1 << XCHAL_MMU_CA_BITS) - 1); + valid_chr = asid ? 'V' : 'I'; + + /* Compute and incorporate the effect of the index bits on the + * va. It's more useful for kernel debugging, since we always + * want to know the effective va anyway. */ + + va += index << way_info->pgsz_log2; + + printk ("\t[%d,%d] (%c) vpn 0x%.8x ppn 0x%.8x asid 0x%.2x am 0x%x\n", + way, index, valid_chr, va, pa, asid, ca); +} + +void print_itlb_entry (struct way_config_t *way_info, int way, int index) +{ + print_entry (way_info, way, index, + read_itlb_virtual (way + (index << way_info->pgsz_log2)), + read_itlb_translation (way + (index << way_info->pgsz_log2))); +} + +void print_dtlb_entry (struct way_config_t *way_info, int way, int index) +{ + print_entry (way_info, way, index, + read_dtlb_virtual (way + (index << way_info->pgsz_log2)), + read_dtlb_translation (way + (index << way_info->pgsz_log2))); +} + +void dump_itlb (void) +{ + int way, index; + + printk ("\nITLB: ways = %d\n", XCHAL_ITLB_WAYS); + + for (way = 0; way < XCHAL_ITLB_WAYS; way++) { + printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n", + way, itlb[way].indicies, + itlb[way].pgsz_log2, way_type(itlb[way].arf)); + for (index = 0; index < itlb[way].indicies; index++) { + print_itlb_entry(&itlb[way], way, index); + } + } +} + +void dump_dtlb (void) +{ + int way, index; + + printk ("\nDTLB: ways = %d\n", XCHAL_DTLB_WAYS); + + for (way = 0; way < XCHAL_DTLB_WAYS; way++) { + printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n", + way, dtlb[way].indicies, + dtlb[way].pgsz_log2, way_type(dtlb[way].arf)); + for (index = 0; index < dtlb[way].indicies; index++) { + print_dtlb_entry(&dtlb[way], way, index); + } + } +} + +void dump_tlb (tlb_dump_entry_t *tinfo, struct way_config_t *config, + int entries, int ways, int type, int show_invalid) +{ + tlb_dump_entry_t *e = tinfo; + int way, i; + + /* Gather all info: */ + for (way = 0; way < ways; way++) { + struct way_config_t *cfg = config + way; + for (i = 0; i < cfg->indicies; i++) { + unsigned wayindex = way + (i << cfg->pgsz_log2); + unsigned vv = (type ? read_dtlb_virtual (wayindex) + : read_itlb_virtual (wayindex)); + unsigned pp = (type ? read_dtlb_translation (wayindex) + : read_itlb_translation (wayindex)); + + /* Compute and incorporate the effect of the index bits on the + * va. It's more useful for kernel debugging, since we always + * want to know the effective va anyway. */ + + e->va = (vv & ~((1 << (cfg->pgsz_log2 + cfg->indicies_log2)) - 1)); + e->va += (i << cfg->pgsz_log2); + e->pa = (pp & ~((1 << cfg->pgsz_log2) - 1)); + e->asid = (vv & ((1 << XCHAL_MMU_ASID_BITS) - 1)); + e->ca = (pp & ((1 << XCHAL_MMU_CA_BITS) - 1)); + e->way = way; + e->index = i; + e->pgsz_log2 = cfg->pgsz_log2; + e->type = type; + e++; + } + } +#if 1 + /* Sort by ASID and VADDR: */ + sort_tlb_dump_info (tinfo, entries); +#endif + + /* Display all sorted info: */ + printk ("\n%cTLB dump:\n", (type ? 'D' : 'I')); + for (e = tinfo, i = 0; i < entries; i++, e++) { +#if 0 + if (e->asid == 0 && !show_invalid) + continue; +#endif + printk ("%c way=%d i=%d ASID=%02X V=%08X -> P=%08X CA=%X (%d %cB)\n", + (e->type ? 'D' : 'I'), e->way, e->index, + e->asid, e->va, e->pa, e->ca, + (1 << (e->pgsz_log2 % 10)), + " kMG"[e->pgsz_log2 / 10] + ); + } +} + +void dump_tlbs2 (int showinv) +{ + dump_tlb (itlb_dump_info, itlb, ITLB_TOTAL_ENTRIES, XCHAL_ITLB_WAYS, 0, showinv); + dump_tlb (dtlb_dump_info, dtlb, DTLB_TOTAL_ENTRIES, XCHAL_DTLB_WAYS, 1, showinv); +} + +void dump_all_tlbs (void) +{ + dump_tlbs2 (1); +} + +void dump_valid_tlbs (void) +{ + dump_tlbs2 (0); +} + + +void dump_tlbs (void) +{ + dump_itlb(); + dump_dtlb(); +} + +void dump_cache_tag(int dcache, int idx) +{ + int w, i, s, e; + unsigned long tag, index; + unsigned long num_lines, num_ways, cache_size, line_size; + + num_ways = dcache ? XCHAL_DCACHE_WAYS : XCHAL_ICACHE_WAYS; + cache_size = dcache ? XCHAL_DCACHE_SIZE : XCHAL_ICACHE_SIZE; + line_size = dcache ? XCHAL_DCACHE_LINESIZE : XCHAL_ICACHE_LINESIZE; + + num_lines = cache_size / num_ways; + + s = 0; e = num_lines; + + if (idx >= 0) + e = (s = idx * line_size) + 1; + + for (i = s; i < e; i+= line_size) { + printk("\nline %#08x:", i); + for (w = 0; w < num_ways; w++) { + index = w * num_lines + i; + if (dcache) + __asm__ __volatile__("ldct %0, %1\n\t" + : "=a"(tag) : "a"(index)); + else + __asm__ __volatile__("lict %0, %1\n\t" + : "=a"(tag) : "a"(index)); + + printk(" %#010lx", tag); + } + } + printk ("\n"); +} + +void dump_icache(int index) +{ + unsigned long data, addr; + int w, i; + + const unsigned long num_ways = XCHAL_ICACHE_WAYS; + const unsigned long cache_size = XCHAL_ICACHE_SIZE; + const unsigned long line_size = XCHAL_ICACHE_LINESIZE; + const unsigned long num_lines = cache_size / num_ways / line_size; + + for (w = 0; w < num_ways; w++) { + printk ("\nWay %d", w); + + for (i = 0; i < line_size; i+= 4) { + addr = w * num_lines + index * line_size + i; + __asm__ __volatile__("licw %0, %1\n\t" + : "=a"(data) : "a"(addr)); + printk(" %#010lx", data); + } + } + printk ("\n"); +} + +void dump_cache_tags(void) +{ + printk("Instruction cache\n"); + dump_cache_tag(0, -1); + printk("Data cache\n"); + dump_cache_tag(1, -1); +} + +#endif diff --git a/arch/xtensa/platform-iss/Makefile b/arch/xtensa/platform-iss/Makefile new file mode 100644 index 000000000000..5b394e9620e5 --- /dev/null +++ b/arch/xtensa/platform-iss/Makefile @@ -0,0 +1,13 @@ +# $Id: Makefile,v 1.1.1.1 2002/08/28 16:10:14 aroll Exp $ +# +# Makefile for the Xtensa Instruction Set Simulator (ISS) +# "prom monitor" library routines under Linux. +# +# 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 in the main makefile... + +obj-y = io.o console.o setup.o network.o + diff --git a/arch/xtensa/platform-iss/console.c b/arch/xtensa/platform-iss/console.c new file mode 100644 index 000000000000..9e2b53f6a907 --- /dev/null +++ b/arch/xtensa/platform-iss/console.c @@ -0,0 +1,303 @@ +/* + * arch/xtensa/platform-iss/console.c + * + * 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) 2001-2005 Tensilica Inc. + * Authors Christian Zankel, Joe Taylor + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#endif + +#define SERIAL_MAX_NUM_LINES 1 +#define SERIAL_TIMER_VALUE (20 * HZ) + +static struct tty_driver *serial_driver; +static struct timer_list serial_timer; + +static DEFINE_SPINLOCK(timer_lock); + +int errno; + +static int __simc (int a, int b, int c, int d, int e, int f) +{ + int ret; + __asm__ __volatile__ ("simcall\n" + "mov %0, a2\n" + "mov %1, a3\n" : "=a" (ret), "=a" (errno) + : : "a2", "a3"); + return ret; +} + +static char *serial_version = "0.1"; +static char *serial_name = "ISS serial driver"; + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ + +static void rs_poll(unsigned long); + +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + int line = tty->index; + + if ((line < 0) || (line >= SERIAL_MAX_NUM_LINES)) + return -ENODEV; + + spin_lock(&timer_lock); + + if (tty->count == 1) { + init_timer(&serial_timer); + serial_timer.data = (unsigned long) tty; + serial_timer.function = rs_poll; + mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE); + } + spin_unlock(&timer_lock); + + return 0; +} + + +/* + * ------------------------------------------------------------ + * iss_serial_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + spin_lock(&timer_lock); + if (tty->count == 1) + del_timer_sync(&serial_timer); + spin_unlock(&timer_lock); +} + + +static int rs_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + /* see drivers/char/serialX.c to reference original version */ + + __simc (SYS_write, 1, (unsigned long)buf, count, 0, 0); + return count; +} + +static void rs_poll(unsigned long priv) +{ + struct tty_struct* tty = (struct tty_struct*) priv; + + struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + int i = 0; + unsigned char c; + + spin_lock(&timer_lock); + + while (__simc(SYS_select_one, 0, XTISS_SELECT_ONE_READ, (int)&tv,0,0)){ + __simc (SYS_read, 0, (unsigned long)&c, 1, 0, 0); + tty->flip.count++; + *tty->flip.char_buf_ptr++ = c; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + i++; + } + + if (i) + tty_flip_buffer_push(tty); + + + mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE); + spin_unlock(&timer_lock); +} + + +static void rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + char buf[2]; + + if (!tty) + return; + + buf[0] = ch; + buf[1] = '\0'; /* Is this NULL necessary? */ + __simc (SYS_write, 1, (unsigned long) buf, 1, 0, 0); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ +} + +static int rs_write_room(struct tty_struct *tty) +{ + /* Let's say iss can always accept 2K characters.. */ + return 2 * 1024; +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + /* the iss doesn't buffer characters */ + return 0; +} + +static void rs_hangup(struct tty_struct *tty) +{ + /* Stub, once again.. */ +} + +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + /* Stub, once again.. */ +} + +static int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len = 0; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); + *eof = 1; + + if (off >= len + begin) + return 0; + + *start = page + (off - begin); + return ((count < begin + len - off) ? count : begin + len - off); +} + + +int register_serial(struct serial_struct*); +void unregister_serial(int); + +static struct tty_operations serial_ops = { + .open = rs_open, + .close = rs_close, + .write = rs_write, + .put_char = rs_put_char, + .flush_chars = rs_flush_chars, + .write_room = rs_write_room, + .chars_in_buffer = rs_chars_in_buffer, + .hangup = rs_hangup, + .wait_until_sent = rs_wait_until_sent, + .read_proc = rs_read_proc +}; + +int __init rs_init(void) +{ + serial_driver = alloc_tty_driver(1); + + printk ("%s %s\n", serial_name, serial_version); + + /* Initialize the tty_driver structure */ + + serial_driver->owner = THIS_MODULE; + serial_driver->driver_name = "iss_serial"; + serial_driver->name = "ttyS"; + serial_driver->major = TTY_MAJOR; + serial_driver->minor_start = 64; + serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + serial_driver->subtype = SERIAL_TYPE_NORMAL; + serial_driver->init_termios = tty_std_termios; + serial_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver->flags = TTY_DRIVER_REAL_RAW; + + tty_set_operations(serial_driver, &serial_ops); + + if (tty_register_driver(serial_driver)) + panic("Couldn't register serial driver\n"); + return 0; +} + + +static __exit void rs_exit(void) +{ + int error; + + if ((error = tty_unregister_driver(serial_driver))) + printk("ISS_SERIAL: failed to unregister serial driver (%d)\n", + error); + put_tty_driver(serial_driver); +} + + +/* We use `late_initcall' instead of just `__initcall' as a workaround for + * the fact that (1) simcons_tty_init can't be called before tty_init, + * (2) tty_init is called via `module_init', (3) if statically linked, + * module_init == device_init, and (4) there's no ordering of init lists. + * We can do this easily because simcons is always statically linked, but + * other tty drivers that depend on tty_init and which must use + * `module_init' to declare their init routines are likely to be broken. + */ + +late_initcall(rs_init); + + +#ifdef CONFIG_SERIAL_CONSOLE + +static void iss_console_write(struct console *co, const char *s, unsigned count) +{ + int len = strlen(s); + + if (s != 0 && *s != 0) + __simc (SYS_write, 1, (unsigned long)s, + count < len ? count : len,0,0); +} + +static struct tty_driver* iss_console_device(struct console *c, int *index) +{ + *index = c->index; + return serial_driver; +} + + +static struct console sercons = { + .name = "ttyS", + .write = iss_console_write, + .device = iss_console_device, + .flags = CON_PRINTBUFFER, + .index = -1 +}; + +static int __init iss_console_init(void) +{ + register_console(&sercons); + return 0; +} + +console_initcall(iss_console_init); + +#endif /* CONFIG_SERIAL_CONSOLE */ + diff --git a/arch/xtensa/platform-iss/io.c b/arch/xtensa/platform-iss/io.c new file mode 100644 index 000000000000..5b161a5cb65f --- /dev/null +++ b/arch/xtensa/platform-iss/io.c @@ -0,0 +1,32 @@ +/* This file isn't really needed right now. */ + +#if 0 + +#include +#include + +extern int __simc (); + + +char iss_serial_getc() +{ + char c; + __simc( SYS_read, 0, &c, 1 ); + return c; +} + +void iss_serial_putc( char c ) +{ + __simc( SYS_write, 1, &c, 1 ); +} + +void iss_serial_puts( char *s ) +{ + if( s != 0 && *s != 0 ) + __simc( SYS_write, 1, s, strlen(s) ); +} + +/*#error Need I/O ports to specific hardware!*/ + +#endif + diff --git a/arch/xtensa/platform-iss/network.c b/arch/xtensa/platform-iss/network.c new file mode 100644 index 000000000000..498d7dced1f4 --- /dev/null +++ b/arch/xtensa/platform-iss/network.c @@ -0,0 +1,855 @@ +/* + * + * arch/xtensa/platform-iss/network.c + * + * Platform specific initialization. + * + * Authors: Chris Zankel + * Based on work form the UML team. + * + * Copyright 2005 Tensilica 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "iss-netdev" +#define ETH_MAX_PACKET 1500 +#define ETH_HEADER_OTHER 14 +#define ISS_NET_TIMER_VALUE (2 * HZ) + + +static DEFINE_SPINLOCK(opened_lock); +static LIST_HEAD(opened); + +static DEFINE_SPINLOCK(devices_lock); +static LIST_HEAD(devices); + +/* ------------------------------------------------------------------------- */ + +/* We currently only support the TUNTAP transport protocol. */ + +#define TRANSPORT_TUNTAP_NAME "tuntap" +#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET + +struct tuntap_info { + char dev_name[IFNAMSIZ]; + int fixed_config; + unsigned char gw[ETH_ALEN]; + int fd; +}; + +/* ------------------------------------------------------------------------- */ + + +/* This structure contains out private information for the driver. */ + +struct iss_net_private { + + struct list_head device_list; + struct list_head opened_list; + + spinlock_t lock; + struct net_device *dev; + struct platform_device pdev; + struct timer_list tl; + struct net_device_stats stats; + + struct timer_list timer; + unsigned int timer_val; + + int index; + int mtu; + + unsigned char mac[ETH_ALEN]; + int have_mac; + + struct { + union { + struct tuntap_info tuntap; + } info; + + int (*open)(struct iss_net_private *lp); + void (*close)(struct iss_net_private *lp); + int (*read)(struct iss_net_private *lp, struct sk_buff **skb); + int (*write)(struct iss_net_private *lp, struct sk_buff **skb); + unsigned short (*protocol)(struct sk_buff *skb); + int (*poll)(struct iss_net_private *lp); + } tp; + +}; + +/* ======================= ISS SIMCALL INTERFACE =========================== */ + +/* Note: __simc must _not_ be declared inline! */ + +static int errno; + +static int __simc (int a, int b, int c, int d, int e, int f) +{ + int ret; + __asm__ __volatile__ ("simcall\n" + "mov %0, a2\n" + "mov %1, a3\n" : "=a" (ret), "=a" (errno) + : : "a2", "a3"); + return ret; +} + +static int inline simc_open(char *file, int flags, int mode) +{ + return __simc(SYS_open, (int) file, flags, mode, 0, 0); +} + +static int inline simc_close(int fd) +{ + return __simc(SYS_close, fd, 0, 0, 0, 0); +} + +static int inline simc_ioctl(int fd, int request, void *arg) +{ + return __simc(SYS_ioctl, fd, request, (int) arg, 0, 0); +} + +static int inline simc_read(int fd, void *buf, size_t count) +{ + return __simc(SYS_read, fd, (int) buf, count, 0, 0); +} + +static int inline simc_write(int fd, void *buf, size_t count) +{ + return __simc(SYS_write, fd, (int) buf, count, 0, 0); +} + +static int inline simc_poll(int fd) +{ + struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + + return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&tv,0,0); +} + +/* ================================ HELPERS ================================ */ + + +static char *split_if_spec(char *str, ...) +{ + char **arg, *end; + va_list ap; + + va_start(ap, str); + while ((arg = va_arg(ap, char**)) != NULL) { + if (*str == '\0') + return NULL; + end = strchr(str, ','); + if (end != str) + *arg = str; + if (end == NULL) + return NULL; + *end ++ = '\0'; + str = end; + } + va_end(ap); + return str; +} + + +#if 0 +/* Adjust SKB. */ + +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; +} +#endif + +/* Return the IP address as a string for a given device. */ + +static 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) || ((in = ip->ifa_list) == NULL)) { + printk(KERN_WARNING "Device not assigned an IP address!\n"); + return; + } + + 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; + } +} + +/* Set Ethernet address of the specified device. */ + +static void inline set_ether_mac(void *d, unsigned char *addr) +{ + struct net_device *dev = d; + memcpy(dev->dev_addr, addr, ETH_ALEN); +} + + +/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */ + +static int tuntap_open(struct iss_net_private *lp) +{ + struct ifreq ifr; + char *dev_name = lp->tp.info.tuntap.dev_name; + int err = -EINVAL; + int fd; + + /* We currently only support a fixed configuration. */ + + if (!lp->tp.info.tuntap.fixed_config) + return -EINVAL; + + if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) { /* O_RDWR */ + printk("Failed to open /dev/net/tun, returned %d " + "(errno = %d)\n", fd, errno); + return fd; + } + + memset(&ifr, 0, sizeof ifr); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name - 1); + + if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) { + printk("Failed to set interface, returned %d " + "(errno = %d)\n", err, errno); + simc_close(fd); + return err; + } + + lp->tp.info.tuntap.fd = fd; + return err; +} + +static void tuntap_close(struct iss_net_private *lp) +{ +#if 0 + if (lp->tp.info.tuntap.fixed_config) + iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name); +#endif + simc_close(lp->tp.info.tuntap.fd); + lp->tp.info.tuntap.fd = -1; +} + +static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb) +{ +#if 0 + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if (*skb == NULL) + return -ENOMEM; +#endif + + return simc_read(lp->tp.info.tuntap.fd, + (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER); +} + +static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb) +{ + return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len); +} + +unsigned short tuntap_protocol(struct sk_buff *skb) +{ + return eth_type_trans(skb, skb->dev); +} + +static int tuntap_poll(struct iss_net_private *lp) +{ + return simc_poll(lp->tp.info.tuntap.fd); +} + +/* + * Currently only a device name is supported. + * ethX=tuntap[,[mac address][,[device name]]] + */ + +static int tuntap_probe(struct iss_net_private *lp, int index, char *init) +{ + const int len = strlen(TRANSPORT_TUNTAP_NAME); + char *dev_name = NULL, *mac_str = NULL, *rem = NULL; + + /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */ + + if (strncmp(init, TRANSPORT_TUNTAP_NAME, len)) + return 0; + + if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') { + if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) { + printk("Extra garbage on specification : '%s'\n", rem); + return 0; + } + } else if (*init != '\0') { + printk("Invalid argument: %s. Skipping device!\n", init); + return 0; + } + + if (dev_name) { + strncpy(lp->tp.info.tuntap.dev_name, dev_name, + sizeof lp->tp.info.tuntap.dev_name); + lp->tp.info.tuntap.fixed_config = 1; + } else + strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME); + + +#if 0 + if (setup_etheraddr(mac_str, lp->mac)) + lp->have_mac = 1; +#endif + lp->mtu = TRANSPORT_TUNTAP_MTU; + + //lp->info.tuntap.gate_addr = gate_addr; + + lp->tp.info.tuntap.fd = -1; + + lp->tp.open = tuntap_open; + lp->tp.close = tuntap_close; + lp->tp.read = tuntap_read; + lp->tp.write = tuntap_write; + lp->tp.protocol = tuntap_protocol; + lp->tp.poll = tuntap_poll; + + printk("TUN/TAP backend - "); +#if 0 + if (lp->host.gate_addr != NULL) + printk("IP = %s", lp->host.gate_addr); +#endif + printk("\n"); + + return 1; +} + +/* ================================ ISS NET ================================ */ + +static int iss_net_rx(struct net_device *dev) +{ + struct iss_net_private *lp = dev->priv; + int pkt_len; + struct sk_buff *skb; + + /* Check if there is any new data. */ + + if (lp->tp.poll(lp) == 0) + return 0; + + /* Try to allocate memory, if it fails, try again next round. */ + + if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) { + lp->stats.rx_dropped++; + return 0; + } + + skb_reserve(skb, 2); + + /* Setup skb */ + + skb->dev = dev; + skb->mac.raw = skb->data; + pkt_len = lp->tp.read(lp, &skb); + skb_put(skb, pkt_len); + + if (pkt_len > 0) { + skb_trim(skb, pkt_len); + skb->protocol = lp->tp.protocol(skb); + // netif_rx(skb); + netif_rx_ni(skb); + + lp->stats.rx_bytes += skb->len; + lp->stats.rx_packets++; + return pkt_len; + } + kfree_skb(skb); + return pkt_len; +} + +static int iss_net_poll(void) +{ + struct list_head *ele; + int err, ret = 0; + + spin_lock(&opened_lock); + + list_for_each(ele, &opened) { + struct iss_net_private *lp; + + lp = list_entry(ele, struct iss_net_private, opened_list); + + if (!netif_running(lp->dev)) + break; + + spin_lock(&lp->lock); + + while ((err = iss_net_rx(lp->dev)) > 0) + ret++; + + spin_unlock(&lp->lock); + + if (err < 0) { + printk(KERN_ERR "Device '%s' read returned %d, " + "shutting it down\n", lp->dev->name, err); + dev_close(lp->dev); + } else { + // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); + } + } + + spin_unlock(&opened_lock); + return ret; +} + + +static void iss_net_timer(unsigned long priv) +{ + struct iss_net_private* lp = (struct iss_net_private*) priv; + + spin_lock(&lp->lock); + + iss_net_poll(); + + mod_timer(&lp->timer, jiffies + lp->timer_val); + + spin_unlock(&lp->lock); +} + + +static int iss_net_open(struct net_device *dev) +{ + struct iss_net_private *lp = dev->priv; + char addr[sizeof "255.255.255.255\0"]; + int err; + + spin_lock(&lp->lock); + + if ((err = lp->tp.open(lp)) < 0) + goto out; + + if (!lp->have_mac) { + dev_ip_addr(dev, addr, &lp->mac[2]); + set_ether_mac(dev, lp->mac); + } + + netif_start_queue(dev); + + /* clear buffer - it can happen that the host side of the interface + * is full when we gethere. In this case, new data is never queued, + * SIGIOs never arrive, and the net never works. + */ + while ((err = iss_net_rx(dev)) > 0) + ; + + spin_lock(&opened_lock); + list_add(&lp->opened_list, &opened); + spin_unlock(&opened_lock); + + init_timer(&lp->timer); + lp->timer_val = ISS_NET_TIMER_VALUE; + lp->timer.data = (unsigned long) lp; + lp->timer.function = iss_net_timer; + mod_timer(&lp->timer, jiffies + lp->timer_val); + +out: + spin_unlock(&lp->lock); + return err; +} + +static int iss_net_close(struct net_device *dev) +{ + struct iss_net_private *lp = dev->priv; +printk("iss_net_close!\n"); + netif_stop_queue(dev); + spin_lock(&lp->lock); + + spin_lock(&opened_lock); + list_del(&opened); + spin_unlock(&opened_lock); + + del_timer_sync(&lp->timer); + + lp->tp.close(lp); + + spin_unlock(&lp->lock); + return 0; +} + +static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct iss_net_private *lp = dev->priv; + unsigned long flags; + int len; + + netif_stop_queue(dev); + spin_lock_irqsave(&lp->lock, flags); + + len = lp->tp.write(lp, &skb); + + 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(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len); + } + + spin_unlock_irqrestore(&lp->lock, flags); + + dev_kfree_skb(skb); + return 0; +} + + +static struct net_device_stats *iss_net_get_stats(struct net_device *dev) +{ + struct iss_net_private *lp = dev->priv; + return &lp->stats; +} + +static void iss_net_set_multicast_list(struct net_device *dev) +{ +#if 0 + if (dev->flags & IFF_PROMISC) + return; + else if (dev->mc_count) + dev->flags |= IFF_ALLMULTI; + else + dev->flags &= ~IFF_ALLMULTI; +#endif +} + +static void iss_net_tx_timeout(struct net_device *dev) +{ +#if 0 + dev->trans_start = jiffies; + netif_wake_queue(dev); +#endif +} + +static int iss_net_set_mac(struct net_device *dev, void *addr) +{ +#if 0 + struct iss_net_private *lp = dev->priv; + struct sockaddr *hwaddr = addr; + + spin_lock(&lp->lock); + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + spin_unlock(&lp->lock); +#endif + + return 0; +} + +static int iss_net_change_mtu(struct net_device *dev, int new_mtu) +{ +#if 0 + struct iss_net_private *lp = dev->priv; + int err = 0; + + spin_lock(&lp->lock); + + // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user); + + if (new_mtu < 0) + err = new_mtu; + else + dev->mtu = new_mtu; + + spin_unlock(&lp->lock); + return err; +#endif + return -EINVAL; +} + +static int iss_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ +#if 0 + static const struct ethtool_drvinfo info = { + .cmd = ETHTOOL_GDRVINFO, + .driver = DRIVER_NAME, + .version = "42", + }; + void *useraddr; + u32 ethcmd; + + switch (cmd) { + case SIOCETHTOOL: + useraddr = ifr->ifr_data; + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EINVAL; + } +#endif + return -EINVAL; +} + +void iss_net_user_timer_expire(unsigned long _conn) +{ +} + + +static struct device_driver iss_net_driver = { + .name = DRIVER_NAME, + .bus = &platform_bus_type, +}; + +static int driver_registered; + +static int iss_net_configure(int index, char *init) +{ + struct net_device *dev; + struct iss_net_private *lp; + int err; + + if ((dev = alloc_etherdev(sizeof *lp)) == NULL) { + printk(KERN_ERR "eth_configure: failed to allocate device\n"); + return 1; + } + + /* Initialize private element. */ + + lp = dev->priv; + *lp = ((struct iss_net_private) { + .device_list = LIST_HEAD_INIT(lp->device_list), + .opened_list = LIST_HEAD_INIT(lp->opened_list), + .lock = SPIN_LOCK_UNLOCKED, + .dev = dev, + .index = index, + //.fd = -1, + .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 }, + .have_mac = 0, + }); + + /* + * Try all transport protocols. + * Note: more protocols can be added by adding '&& !X_init(lp, eth)'. + */ + + if (!tuntap_probe(lp, index, init)) { + printk("Invalid arguments. Skipping device!\n"); + goto errout; + } + + printk(KERN_INFO "Netdevice %d ", index); + if (lp->have_mac) + printk("(%02x:%02x:%02x:%02x:%02x:%02x) ", + lp->mac[0], lp->mac[1], + lp->mac[2], lp->mac[3], + lp->mac[4], lp->mac[5]); + printk(": "); + + /* sysfs register */ + + if (!driver_registered) { + driver_register(&iss_net_driver); + driver_registered = 1; + } + + spin_lock(&devices_lock); + list_add(&lp->device_list, &devices); + spin_unlock(&devices_lock); + + lp->pdev.id = index; + lp->pdev.name = DRIVER_NAME; + platform_device_register(&lp->pdev); + SET_NETDEV_DEV(dev,&lp->pdev.dev); + + /* + * If this name ends up conflicting with an existing registered + * netdevice, that is OK, register_netdev{,ice}() will notice this + * and fail. + */ + snprintf(dev->name, sizeof dev->name, "eth%d", index); + + dev->mtu = lp->mtu; + dev->open = iss_net_open; + dev->hard_start_xmit = iss_net_start_xmit; + dev->stop = iss_net_close; + dev->get_stats = iss_net_get_stats; + dev->set_multicast_list = iss_net_set_multicast_list; + dev->tx_timeout = iss_net_tx_timeout; + dev->set_mac_address = iss_net_set_mac; + dev->change_mtu = iss_net_change_mtu; + dev->do_ioctl = iss_net_ioctl; + dev->watchdog_timeo = (HZ >> 1); + dev->irq = -1; + + rtnl_lock(); + err = register_netdevice(dev); + rtnl_unlock(); + + if (err) { + printk("Error registering net device!\n"); + /* XXX: should we call ->remove() here? */ + free_netdev(dev); + return 1; + } + + init_timer(&lp->tl); + lp->tl.function = iss_net_user_timer_expire; + +#if 0 + if (lp->have_mac) + set_ether_mac(dev, lp->mac); +#endif + return 0; + +errout: + // FIXME: unregister; free, etc.. + return -EIO; + +} + +/* ------------------------------------------------------------------------- */ + +/* Filled in during early boot */ + +struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); + +struct iss_net_init { + struct list_head list; + char *init; /* init string */ + int index; +}; + +/* + * Parse the command line and look for 'ethX=...' fields, and register all + * those fields. They will be later initialized in iss_net_init. + */ + +#define ERR KERN_ERR "iss_net_setup: " + +static int iss_net_setup(char *str) +{ + struct iss_net_private *device = NULL; + struct iss_net_init *new; + struct list_head *ele; + char *end; + int n; + + n = simple_strtoul(str, &end, 0); + if (end == str) { + printk(ERR "Failed to parse '%s'\n", str); + return 1; + } + if (n < 0) { + printk(ERR "Device %d is negative\n", n); + return 1; + } + if (*(str = end) != '=') { + printk(ERR "Expected '=' after device number\n"); + return 1; + } + + spin_lock(&devices_lock); + + list_for_each(ele, &devices) { + device = list_entry(ele, struct iss_net_private, device_list); + if (device->index == n) + break; + } + + spin_unlock(&devices_lock); + + if (device && device->index == n) { + printk(ERR "Device %d already configured\n", n); + return 1; + } + + if ((new = alloc_bootmem(sizeof new)) == NULL) { + printk("Alloc_bootmem failed\n"); + return 1; + } + + INIT_LIST_HEAD(&new->list); + new->index = n; + new->init = str + 1; + + list_add_tail(&new->list, ð_cmd_line); + return 1; +} + +#undef ERR + +__setup("eth", iss_net_setup); + +/* + * Initialize all ISS Ethernet devices previously registered in iss_net_setup. + */ + +static int iss_net_init(void) +{ + struct list_head *ele, *next; + + /* Walk through all Ethernet devices specified in the command line. */ + + list_for_each_safe(ele, next, ð_cmd_line) { + struct iss_net_init *eth; + eth = list_entry(ele, struct iss_net_init, list); + iss_net_configure(eth->index, eth->init); + } + + return 1; +} + +module_init(iss_net_init); + diff --git a/arch/xtensa/platform-iss/setup.c b/arch/xtensa/platform-iss/setup.c new file mode 100644 index 000000000000..2e6dcbf0cc04 --- /dev/null +++ b/arch/xtensa/platform-iss/setup.c @@ -0,0 +1,112 @@ +/* + * + * arch/xtensa/platform-iss/setup.c + * + * Platform specific initialization. + * + * Authors: Chris Zankel + * Joe Taylor + * + * Copyright 2001 - 2005 Tensilica 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +void __init platform_init(bp_tag_t* bootparam) +{ + +} + +void platform_halt(void) +{ + printk (" ** Called platform_halt(), looping forever! **\n"); + while (1); +} + +void platform_power_off(void) +{ + printk (" ** Called platform_power_off(), looping forever! **\n"); + while (1); +} +void platform_restart(void) +{ + /* Flush and reset the mmu, simulate a processor reset, and + * jump to the reset vector. */ + + __asm__ __volatile__("movi a2, 15\n\t" + "wsr a2, " __stringify(ICOUNTLEVEL) "\n\t" + "movi a2, 0\n\t" + "wsr a2, " __stringify(ICOUNT) "\n\t" + "wsr a2, " __stringify(IBREAKENABLE) "\n\t" + "wsr a2, " __stringify(LCOUNT) "\n\t" + "movi a2, 0x1f\n\t" + "wsr a2, " __stringify(PS) "\n\t" + "isync\n\t" + "jx %0\n\t" + : + : "a" (XCHAL_RESET_VECTOR_VADDR) + : "a2"); + + /* control never gets here */ +} + +extern void iss_net_poll(void); + +const char twirl[]="|/-\\|/-\\"; + +void platform_heartbeat(void) +{ +#if 0 + static int i = 0, j = 0; + + if (--i < 0) { + i = 99; + printk("\r%c\r", twirl[j++]); + if (j == 8) + j = 0; + } +#endif +} + + + +static int +iss_panic_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + __asm__ __volatile__("movi a2, -1; simcall\n"); + return NOTIFY_DONE; +} + +static struct notifier_block iss_panic_block = { + iss_panic_event, + NULL, + 0 +}; + +void __init platform_setup(char **p_cmdline) +{ + notifier_chain_register(&panic_notifier_list, &iss_panic_block); +} diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index 638db06de2be..3410b4d294b9 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -1871,20 +1871,22 @@ static int as_init_queue(request_queue_t *q, elevator_t *e) if (!arq_pool) return -ENOMEM; - ad = kmalloc(sizeof(*ad), GFP_KERNEL); + ad = kmalloc_node(sizeof(*ad), GFP_KERNEL, q->node); if (!ad) return -ENOMEM; memset(ad, 0, sizeof(*ad)); ad->q = q; /* Identify what queue the data belongs to */ - ad->hash = kmalloc(sizeof(struct list_head)*AS_HASH_ENTRIES,GFP_KERNEL); + ad->hash = kmalloc_node(sizeof(struct list_head)*AS_HASH_ENTRIES, + GFP_KERNEL, q->node); if (!ad->hash) { kfree(ad); return -ENOMEM; } - ad->arq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, arq_pool); + ad->arq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, + mempool_free_slab, arq_pool, q->node); if (!ad->arq_pool) { kfree(ad->hash); kfree(ad); diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index 7f79f3dd0165..4bc2fea73273 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -711,18 +711,20 @@ static int deadline_init_queue(request_queue_t *q, elevator_t *e) if (!drq_pool) return -ENOMEM; - dd = kmalloc(sizeof(*dd), GFP_KERNEL); + dd = kmalloc_node(sizeof(*dd), GFP_KERNEL, q->node); if (!dd) return -ENOMEM; memset(dd, 0, sizeof(*dd)); - dd->hash = kmalloc(sizeof(struct list_head)*DL_HASH_ENTRIES,GFP_KERNEL); + dd->hash = kmalloc_node(sizeof(struct list_head)*DL_HASH_ENTRIES, + GFP_KERNEL, q->node); if (!dd->hash) { kfree(dd); return -ENOMEM; } - dd->drq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, drq_pool); + dd->drq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, + mempool_free_slab, drq_pool, q->node); if (!dd->drq_pool) { kfree(dd->hash); kfree(dd); diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 53f7d846b747..47fd3659a061 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -40,7 +40,7 @@ static inline int major_to_index(int major) #ifdef CONFIG_PROC_FS /* get block device names in somewhat random order */ -int get_blkdev_list(char *p) +int get_blkdev_list(char *p, int used) { struct blk_major_name *n; int i, len; @@ -49,10 +49,18 @@ int get_blkdev_list(char *p) down(&block_subsys_sem); for (i = 0; i < ARRAY_SIZE(major_names); i++) { - for (n = major_names[i]; n; n = n->next) + for (n = major_names[i]; n; n = n->next) { + /* + * If the curent string plus the 5 extra characters + * in the line would run us off the page, then we're done + */ + if ((len + used + strlen(n->name) + 5) >= PAGE_SIZE) + goto page_full; len += sprintf(p+len, "%3d %s\n", n->major, n->name); + } } +page_full: up(&block_subsys_sem); return len; @@ -582,10 +590,16 @@ struct seq_operations diskstats_op = { .show = diskstats_show }; - struct gendisk *alloc_disk(int minors) { - struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); + return alloc_disk_node(minors, -1); +} + +struct gendisk *alloc_disk_node(int minors, int node_id) +{ + struct gendisk *disk; + + disk = kmalloc_node(sizeof(struct gendisk), GFP_KERNEL, node_id); if (disk) { memset(disk, 0, sizeof(struct gendisk)); if (!init_disk_stats(disk)) { @@ -594,7 +608,7 @@ struct gendisk *alloc_disk(int minors) } if (minors > 1) { int size = (minors - 1) * sizeof(struct hd_struct *); - disk->part = kmalloc(size, GFP_KERNEL); + disk->part = kmalloc_node(size, GFP_KERNEL, node_id); if (!disk->part) { kfree(disk); return NULL; @@ -610,6 +624,7 @@ struct gendisk *alloc_disk(int minors) } EXPORT_SYMBOL(alloc_disk); +EXPORT_SYMBOL(alloc_disk_node); struct kobject *get_disk(struct gendisk *disk) { diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c index 6d7bcc9da9e7..6e278474f9a8 100644 --- a/drivers/block/ioctl.c +++ b/drivers/block/ioctl.c @@ -133,11 +133,9 @@ static int put_u64(unsigned long arg, u64 val) return put_user(val, (u64 __user *)arg); } -int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, - unsigned long arg) +static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev, + unsigned cmd, unsigned long arg) { - struct block_device *bdev = inode->i_bdev; - struct gendisk *disk = bdev->bd_disk; struct backing_dev_info *bdi; int ret, n; @@ -190,36 +188,72 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, return put_ulong(arg, bdev->bd_inode->i_size >> 9); case BLKGETSIZE64: return put_u64(arg, bdev->bd_inode->i_size); + } + return -ENOIOCTLCMD; +} + +static int blkdev_driver_ioctl(struct inode *inode, struct file *file, + struct gendisk *disk, unsigned cmd, unsigned long arg) +{ + int ret; + if (disk->fops->unlocked_ioctl) + return disk->fops->unlocked_ioctl(file, cmd, arg); + + if (disk->fops->ioctl) { + lock_kernel(); + ret = disk->fops->ioctl(inode, file, cmd, arg); + unlock_kernel(); + return ret; + } + + return -ENOTTY; +} + +int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, + unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct gendisk *disk = bdev->bd_disk; + int ret, n; + + switch(cmd) { case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (disk->fops->ioctl) { - ret = disk->fops->ioctl(inode, file, cmd, arg); - /* -EINVAL to handle old uncorrected drivers */ - if (ret != -EINVAL && ret != -ENOTTY) - return ret; - } + + ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); + /* -EINVAL to handle old uncorrected drivers */ + if (ret != -EINVAL && ret != -ENOTTY) + return ret; + + lock_kernel(); fsync_bdev(bdev); invalidate_bdev(bdev, 0); + unlock_kernel(); return 0; + case BLKROSET: - if (disk->fops->ioctl) { - ret = disk->fops->ioctl(inode, file, cmd, arg); - /* -EINVAL to handle old uncorrected drivers */ - if (ret != -EINVAL && ret != -ENOTTY) - return ret; - } + ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); + /* -EINVAL to handle old uncorrected drivers */ + if (ret != -EINVAL && ret != -ENOTTY) + return ret; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (get_user(n, (int __user *)(arg))) return -EFAULT; + lock_kernel(); set_device_ro(bdev, n); + unlock_kernel(); return 0; - default: - if (disk->fops->ioctl) - return disk->fops->ioctl(inode, file, cmd, arg); } - return -ENOTTY; + + lock_kernel(); + ret = blkdev_locked_ioctl(file, bdev, cmd, arg); + unlock_kernel(); + if (ret != -ENOIOCTLCMD) + return ret; + + return blkdev_driver_ioctl(inode, file, disk, cmd, arg); } /* Most of the generic ioctls are handled in the normal fallback path. diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 81fe3a0c1fe7..fd94ea27d594 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -28,6 +28,7 @@ #include #include #include +#include /* * for max sense size @@ -716,7 +717,7 @@ struct request *blk_queue_find_tag(request_queue_t *q, int tag) { struct blk_queue_tag *bqt = q->queue_tags; - if (unlikely(bqt == NULL || tag >= bqt->real_max_depth)) + if (unlikely(bqt == NULL || tag >= bqt->max_depth)) return NULL; return bqt->tag_index[tag]; @@ -774,9 +775,9 @@ EXPORT_SYMBOL(blk_queue_free_tags); static int init_tag_map(request_queue_t *q, struct blk_queue_tag *tags, int depth) { - int bits, i; struct request **tag_index; unsigned long *tag_map; + int nr_ulongs; if (depth > q->nr_requests * 2) { depth = q->nr_requests * 2; @@ -788,24 +789,17 @@ init_tag_map(request_queue_t *q, struct blk_queue_tag *tags, int depth) if (!tag_index) goto fail; - bits = (depth / BLK_TAGS_PER_LONG) + 1; - tag_map = kmalloc(bits * sizeof(unsigned long), GFP_ATOMIC); + nr_ulongs = ALIGN(depth, BITS_PER_LONG) / BITS_PER_LONG; + tag_map = kmalloc(nr_ulongs * sizeof(unsigned long), GFP_ATOMIC); if (!tag_map) goto fail; memset(tag_index, 0, depth * sizeof(struct request *)); - memset(tag_map, 0, bits * sizeof(unsigned long)); + memset(tag_map, 0, nr_ulongs * sizeof(unsigned long)); tags->max_depth = depth; - tags->real_max_depth = bits * BITS_PER_LONG; tags->tag_index = tag_index; tags->tag_map = tag_map; - /* - * set the upper bits if the depth isn't a multiple of the word size - */ - for (i = depth; i < bits * BLK_TAGS_PER_LONG; i++) - __set_bit(i, tag_map); - return 0; fail: kfree(tag_index); @@ -870,32 +864,24 @@ int blk_queue_resize_tags(request_queue_t *q, int new_depth) struct blk_queue_tag *bqt = q->queue_tags; struct request **tag_index; unsigned long *tag_map; - int bits, max_depth; + int max_depth, nr_ulongs; if (!bqt) return -ENXIO; - /* - * don't bother sizing down - */ - if (new_depth <= bqt->real_max_depth) { - bqt->max_depth = new_depth; - return 0; - } - /* * save the old state info, so we can copy it back */ tag_index = bqt->tag_index; tag_map = bqt->tag_map; - max_depth = bqt->real_max_depth; + max_depth = bqt->max_depth; if (init_tag_map(q, bqt, new_depth)) return -ENOMEM; memcpy(bqt->tag_index, tag_index, max_depth * sizeof(struct request *)); - bits = max_depth / BLK_TAGS_PER_LONG; - memcpy(bqt->tag_map, tag_map, bits * sizeof(unsigned long)); + nr_ulongs = ALIGN(max_depth, BITS_PER_LONG) / BITS_PER_LONG; + memcpy(bqt->tag_map, tag_map, nr_ulongs * sizeof(unsigned long)); kfree(tag_index); kfree(tag_map); @@ -925,11 +911,16 @@ void blk_queue_end_tag(request_queue_t *q, struct request *rq) BUG_ON(tag == -1); - if (unlikely(tag >= bqt->real_max_depth)) + if (unlikely(tag >= bqt->max_depth)) + /* + * This can happen after tag depth has been reduced. + * FIXME: how about a warning or info message here? + */ return; if (unlikely(!__test_and_clear_bit(tag, bqt->tag_map))) { - printk("attempt to clear non-busy tag (%d)\n", tag); + printk(KERN_ERR "%s: attempt to clear non-busy tag (%d)\n", + __FUNCTION__, tag); return; } @@ -938,7 +929,8 @@ void blk_queue_end_tag(request_queue_t *q, struct request *rq) rq->tag = -1; if (unlikely(bqt->tag_index[tag] == NULL)) - printk("tag %d is missing\n", tag); + printk(KERN_ERR "%s: tag %d is missing\n", + __FUNCTION__, tag); bqt->tag_index[tag] = NULL; bqt->busy--; @@ -967,24 +959,20 @@ EXPORT_SYMBOL(blk_queue_end_tag); int blk_queue_start_tag(request_queue_t *q, struct request *rq) { struct blk_queue_tag *bqt = q->queue_tags; - unsigned long *map = bqt->tag_map; - int tag = 0; + int tag; if (unlikely((rq->flags & REQ_QUEUED))) { printk(KERN_ERR - "request %p for device [%s] already tagged %d", - rq, rq->rq_disk ? rq->rq_disk->disk_name : "?", rq->tag); + "%s: request %p for device [%s] already tagged %d", + __FUNCTION__, rq, + rq->rq_disk ? rq->rq_disk->disk_name : "?", rq->tag); BUG(); } - for (map = bqt->tag_map; *map == -1UL; map++) { - tag += BLK_TAGS_PER_LONG; + tag = find_first_zero_bit(bqt->tag_map, bqt->max_depth); + if (tag >= bqt->max_depth) + return 1; - if (tag >= bqt->max_depth) - return 1; - } - - tag += ffz(*map); __set_bit(tag, bqt->tag_map); rq->flags |= REQ_QUEUED; @@ -1020,7 +1008,8 @@ void blk_queue_invalidate_tags(request_queue_t *q) rq = list_entry_rq(tmp); if (rq->tag == -1) { - printk("bad tag found on list\n"); + printk(KERN_ERR + "%s: bad tag found on list\n", __FUNCTION__); list_del_init(&rq->queuelist); rq->flags &= ~REQ_QUEUED; } else @@ -1450,7 +1439,7 @@ EXPORT_SYMBOL(blk_remove_plug); */ void __generic_unplug_device(request_queue_t *q) { - if (test_bit(QUEUE_FLAG_STOPPED, &q->queue_flags)) + if (unlikely(test_bit(QUEUE_FLAG_STOPPED, &q->queue_flags))) return; if (!blk_remove_plug(q)) @@ -1645,7 +1634,8 @@ static int blk_init_free_list(request_queue_t *q) init_waitqueue_head(&rl->wait[WRITE]); init_waitqueue_head(&rl->drain); - rl->rq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep); + rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, + mempool_free_slab, request_cachep, q->node); if (!rl->rq_pool) return -ENOMEM; @@ -1657,8 +1647,15 @@ static int __make_request(request_queue_t *, struct bio *); request_queue_t *blk_alloc_queue(int gfp_mask) { - request_queue_t *q = kmem_cache_alloc(requestq_cachep, gfp_mask); + return blk_alloc_queue_node(gfp_mask, -1); +} +EXPORT_SYMBOL(blk_alloc_queue); +request_queue_t *blk_alloc_queue_node(int gfp_mask, int node_id) +{ + request_queue_t *q; + + q = kmem_cache_alloc_node(requestq_cachep, gfp_mask, node_id); if (!q) return NULL; @@ -1671,8 +1668,7 @@ request_queue_t *blk_alloc_queue(int gfp_mask) return q; } - -EXPORT_SYMBOL(blk_alloc_queue); +EXPORT_SYMBOL(blk_alloc_queue_node); /** * blk_init_queue - prepare a request queue for use with a block device @@ -1705,13 +1701,22 @@ EXPORT_SYMBOL(blk_alloc_queue); * blk_init_queue() must be paired with a blk_cleanup_queue() call * when the block device is deactivated (such as at module unload). **/ + request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) { - request_queue_t *q = blk_alloc_queue(GFP_KERNEL); + return blk_init_queue_node(rfn, lock, -1); +} +EXPORT_SYMBOL(blk_init_queue); + +request_queue_t * +blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id) +{ + request_queue_t *q = blk_alloc_queue_node(GFP_KERNEL, node_id); if (!q) return NULL; + q->node = node_id; if (blk_init_free_list(q)) goto out_init; @@ -1754,12 +1759,11 @@ out_init: kmem_cache_free(requestq_cachep, q); return NULL; } - -EXPORT_SYMBOL(blk_init_queue); +EXPORT_SYMBOL(blk_init_queue_node); int blk_get_queue(request_queue_t *q) { - if (!test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)) { + if (likely(!test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) { atomic_inc(&q->refcnt); return 0; } @@ -1838,7 +1842,6 @@ static void __freed_request(request_queue_t *q, int rw) clear_queue_congested(q, rw); if (rl->count[rw] + 1 <= q->nr_requests) { - smp_mb(); if (waitqueue_active(&rl->wait[rw])) wake_up(&rl->wait[rw]); @@ -1966,7 +1969,6 @@ static struct request *get_request_wait(request_queue_t *q, int rw) DEFINE_WAIT(wait); struct request *rq; - generic_unplug_device(q); do { struct request_list *rl = &q->rq; @@ -1978,6 +1980,7 @@ static struct request *get_request_wait(request_queue_t *q, int rw) if (!rq) { struct io_context *ioc; + generic_unplug_device(q); io_schedule(); /* @@ -2581,7 +2584,7 @@ static int __make_request(request_queue_t *q, struct bio *bio) spin_lock_prefetch(q->queue_lock); barrier = bio_barrier(bio); - if (barrier && (q->ordered == QUEUE_ORDERED_NONE)) { + if (unlikely(barrier) && (q->ordered == QUEUE_ORDERED_NONE)) { err = -EOPNOTSUPP; goto end_io; } @@ -2682,7 +2685,7 @@ get_rq: /* * REQ_BARRIER implies no merging, but lets make it explicit */ - if (barrier) + if (unlikely(barrier)) req->flags |= (REQ_HARDBARRIER | REQ_NOMERGE); req->errors = 0; @@ -2806,7 +2809,7 @@ static inline void block_wait_queue_running(request_queue_t *q) { DEFINE_WAIT(wait); - while (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) { + while (unlikely(test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags))) { struct request_list *rl = &q->rq; prepare_to_wait_exclusive(&rl->drain, &wait, @@ -2915,7 +2918,7 @@ end_io: goto end_io; } - if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)) + if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) goto end_io; block_wait_queue_running(q); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 6f011d0d8e97..b35e08876dd4 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -472,17 +472,11 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) */ static void loop_add_bio(struct loop_device *lo, struct bio *bio) { - unsigned long flags; - - spin_lock_irqsave(&lo->lo_lock, flags); if (lo->lo_biotail) { lo->lo_biotail->bi_next = bio; lo->lo_biotail = bio; } else lo->lo_bio = lo->lo_biotail = bio; - spin_unlock_irqrestore(&lo->lo_lock, flags); - - up(&lo->lo_bh_mutex); } /* @@ -492,14 +486,12 @@ static struct bio *loop_get_bio(struct loop_device *lo) { struct bio *bio; - spin_lock_irq(&lo->lo_lock); if ((bio = lo->lo_bio)) { if (bio == lo->lo_biotail) lo->lo_biotail = NULL; lo->lo_bio = bio->bi_next; bio->bi_next = NULL; } - spin_unlock_irq(&lo->lo_lock); return bio; } @@ -509,35 +501,28 @@ static int loop_make_request(request_queue_t *q, struct bio *old_bio) struct loop_device *lo = q->queuedata; int rw = bio_rw(old_bio); - if (!lo) - goto out; + if (rw == READA) + rw = READ; + + BUG_ON(!lo || (rw != READ && rw != WRITE)); spin_lock_irq(&lo->lo_lock); if (lo->lo_state != Lo_bound) - goto inactive; - atomic_inc(&lo->lo_pending); - spin_unlock_irq(&lo->lo_lock); - - if (rw == WRITE) { - if (lo->lo_flags & LO_FLAGS_READ_ONLY) - goto err; - } else if (rw == READA) { - rw = READ; - } else if (rw != READ) { - printk(KERN_ERR "loop: unknown command (%x)\n", rw); - goto err; - } + goto out; + if (unlikely(rw == WRITE && (lo->lo_flags & LO_FLAGS_READ_ONLY))) + goto out; + lo->lo_pending++; loop_add_bio(lo, old_bio); + spin_unlock_irq(&lo->lo_lock); + up(&lo->lo_bh_mutex); return 0; -err: - if (atomic_dec_and_test(&lo->lo_pending)) - up(&lo->lo_bh_mutex); + out: + if (lo->lo_pending == 0) + up(&lo->lo_bh_mutex); + spin_unlock_irq(&lo->lo_lock); bio_io_error(old_bio, old_bio->bi_size); return 0; -inactive: - spin_unlock_irq(&lo->lo_lock); - goto out; } /* @@ -560,13 +545,11 @@ static void do_loop_switch(struct loop_device *, struct switch_request *); static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio) { - int ret; - if (unlikely(!bio->bi_bdev)) { do_loop_switch(lo, bio->bi_private); bio_put(bio); } else { - ret = do_bio_filebacked(lo, bio); + int ret = do_bio_filebacked(lo, bio); bio_endio(bio, bio->bi_size, ret); } } @@ -594,7 +577,7 @@ static int loop_thread(void *data) set_user_nice(current, -20); lo->lo_state = Lo_bound; - atomic_inc(&lo->lo_pending); + lo->lo_pending = 1; /* * up sem, we are running @@ -602,26 +585,37 @@ static int loop_thread(void *data) up(&lo->lo_sem); for (;;) { - down_interruptible(&lo->lo_bh_mutex); + int pending; + /* - * could be upped because of tear-down, not because of - * pending work + * interruptible just to not contribute to load avg */ - if (!atomic_read(&lo->lo_pending)) + if (down_interruptible(&lo->lo_bh_mutex)) + continue; + + spin_lock_irq(&lo->lo_lock); + + /* + * could be upped because of tear-down, not pending work + */ + if (unlikely(!lo->lo_pending)) { + spin_unlock_irq(&lo->lo_lock); break; + } bio = loop_get_bio(lo); - if (!bio) { - printk("loop: missing bio\n"); - continue; - } + lo->lo_pending--; + pending = lo->lo_pending; + spin_unlock_irq(&lo->lo_lock); + + BUG_ON(!bio); loop_handle_bio(lo, bio); /* * upped both for pending work and tear-down, lo_pending * will hit zero then */ - if (atomic_dec_and_test(&lo->lo_pending)) + if (unlikely(!pending)) break; } @@ -900,7 +894,8 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) spin_lock_irq(&lo->lo_lock); lo->lo_state = Lo_rundown; - if (atomic_dec_and_test(&lo->lo_pending)) + lo->lo_pending--; + if (!lo->lo_pending) up(&lo->lo_bh_mutex); spin_unlock_irq(&lo->lo_lock); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index bc56770bcc90..7f3d78de265c 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -467,14 +467,12 @@ static int pkt_set_speed(struct pktcdvd_device *pd, unsigned write_speed, unsign * Queue a bio for processing by the low-level CD device. Must be called * from process context. */ -static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio, int high_prio_read) +static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio) { spin_lock(&pd->iosched.lock); if (bio_data_dir(bio) == READ) { pkt_add_list_last(bio, &pd->iosched.read_queue, &pd->iosched.read_queue_tail); - if (high_prio_read) - pd->iosched.high_prio_read = 1; } else { pkt_add_list_last(bio, &pd->iosched.write_queue, &pd->iosched.write_queue_tail); @@ -490,15 +488,16 @@ static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio, int high_p * requirements for CDRW drives: * - A cache flush command must be inserted before a read request if the * previous request was a write. - * - Switching between reading and writing is slow, so don't it more often + * - Switching between reading and writing is slow, so don't do it more often * than necessary. + * - Optimize for throughput at the expense of latency. This means that streaming + * writes will never be interrupted by a read, but if the drive has to seek + * before the next write, switch to reading instead if there are any pending + * read requests. * - Set the read speed according to current usage pattern. When only reading * from the device, it's best to use the highest possible read speed, but * when switching often between reading and writing, it's better to have the * same read and write speeds. - * - Reads originating from user space should have higher priority than reads - * originating from pkt_gather_data, because some process is usually waiting - * on reads of the first kind. */ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) { @@ -512,21 +511,24 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) for (;;) { struct bio *bio; - int reads_queued, writes_queued, high_prio_read; + int reads_queued, writes_queued; spin_lock(&pd->iosched.lock); reads_queued = (pd->iosched.read_queue != NULL); writes_queued = (pd->iosched.write_queue != NULL); - if (!reads_queued) - pd->iosched.high_prio_read = 0; - high_prio_read = pd->iosched.high_prio_read; spin_unlock(&pd->iosched.lock); if (!reads_queued && !writes_queued) break; if (pd->iosched.writing) { - if (high_prio_read || (!writes_queued && reads_queued)) { + int need_write_seek = 1; + spin_lock(&pd->iosched.lock); + bio = pd->iosched.write_queue; + spin_unlock(&pd->iosched.lock); + if (bio && (bio->bi_sector == pd->iosched.last_write)) + need_write_seek = 0; + if (need_write_seek && reads_queued) { if (atomic_read(&pd->cdrw.pending_bios) > 0) { VPRINTK("pktcdvd: write, waiting\n"); break; @@ -559,8 +561,10 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) if (bio_data_dir(bio) == READ) pd->iosched.successive_reads += bio->bi_size >> 10; - else + else { pd->iosched.successive_reads = 0; + pd->iosched.last_write = bio->bi_sector + bio_sectors(bio); + } if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) { if (pd->read_speed == pd->write_speed) { pd->read_speed = MAX_SPEED; @@ -765,7 +769,7 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) atomic_inc(&pkt->io_wait); bio->bi_rw = READ; - pkt_queue_bio(pd, bio, 0); + pkt_queue_bio(pd, bio); frames_read++; } @@ -1062,7 +1066,7 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt) atomic_set(&pkt->io_wait, 1); pkt->w_bio->bi_rw = WRITE; - pkt_queue_bio(pd, pkt->w_bio, 0); + pkt_queue_bio(pd, pkt->w_bio); } static void pkt_finish_packet(struct packet_data *pkt, int uptodate) @@ -2120,7 +2124,7 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) cloned_bio->bi_private = psd; cloned_bio->bi_end_io = pkt_end_io_read_cloned; pd->stats.secs_r += bio->bi_size >> 9; - pkt_queue_bio(pd, cloned_bio, 1); + pkt_queue_bio(pd, cloned_bio); return 0; } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 9075bbb56ad4..f766bc22c6bb 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -576,7 +576,7 @@ static void __exit hci_uart_exit(void) #endif /* Release tty registration of line discipline */ - if ((err = tty_register_ldisc(N_HCI, NULL))) + if ((err = tty_unregister_ldisc(N_HCI))) BT_ERR("Can't unregister HCI line discipline (%d)", err); } diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 1dc4259213a6..777bc499bbbd 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -861,13 +861,18 @@ static void change_speed(struct async_struct *info, static void rs_put_char(struct tty_struct *tty, unsigned char ch) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct async_struct *info; unsigned long flags; + if (!tty) + return; + + info = tty->driver_data; + if (serial_paranoia_check(info, tty->name, "rs_put_char")) return; - if (!tty || !info->xmit.buf) + if (!info->xmit.buf) return; local_irq_save(flags); @@ -910,13 +915,18 @@ static void rs_flush_chars(struct tty_struct *tty) static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count) { int c, ret = 0; - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct async_struct *info; unsigned long flags; + if (!tty) + return 0; + + info = tty->driver_data; + if (serial_paranoia_check(info, tty->name, "rs_write")) return 0; - if (!tty || !info->xmit.buf || !tmp_buf) + if (!info->xmit.buf || !tmp_buf) return 0; local_save_flags(flags); diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 88d1ad656e99..e0a53570fea1 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -45,6 +45,7 @@ #include #include #include +#include #define IPMI_DEVINTF_VERSION "v33" @@ -500,10 +501,205 @@ static int ipmi_ioctl(struct inode *inode, return rv; } +#ifdef CONFIG_COMPAT + +/* + * The following code contains code for supporting 32-bit compatible + * ioctls on 64-bit kernels. This allows running 32-bit apps on the + * 64-bit kernel + */ +#define COMPAT_IPMICTL_SEND_COMMAND \ + _IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req) +#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME \ + _IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime) +#define COMPAT_IPMICTL_RECEIVE_MSG \ + _IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv) +#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC \ + _IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv) + +struct compat_ipmi_msg { + u8 netfn; + u8 cmd; + u16 data_len; + compat_uptr_t data; +}; + +struct compat_ipmi_req { + compat_uptr_t addr; + compat_uint_t addr_len; + compat_long_t msgid; + struct compat_ipmi_msg msg; +}; + +struct compat_ipmi_recv { + compat_int_t recv_type; + compat_uptr_t addr; + compat_uint_t addr_len; + compat_long_t msgid; + struct compat_ipmi_msg msg; +}; + +struct compat_ipmi_req_settime { + struct compat_ipmi_req req; + compat_int_t retries; + compat_uint_t retry_time_ms; +}; + +/* + * Define some helper functions for copying IPMI data + */ +static long get_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg __user *p32) +{ + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(p64->netfn, &p32->netfn) || + __get_user(p64->cmd, &p32->cmd) || + __get_user(p64->data_len, &p32->data_len) || + __get_user(tmp, &p32->data)) + return -EFAULT; + p64->data = compat_ptr(tmp); + return 0; +} + +static long put_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg __user *p32) +{ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + __put_user(p64->netfn, &p32->netfn) || + __put_user(p64->cmd, &p32->cmd) || + __put_user(p64->data_len, &p32->data_len)) + return -EFAULT; + return 0; +} + +static long get_compat_ipmi_req(struct ipmi_req *p64, + struct compat_ipmi_req __user *p32) +{ + + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(tmp, &p32->addr) || + __get_user(p64->addr_len, &p32->addr_len) || + __get_user(p64->msgid, &p32->msgid) || + get_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + p64->addr = compat_ptr(tmp); + return 0; +} + +static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64, + struct compat_ipmi_req_settime __user *p32) +{ + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_compat_ipmi_req(&p64->req, &p32->req) || + __get_user(p64->retries, &p32->retries) || + __get_user(p64->retry_time_ms, &p32->retry_time_ms)) + return -EFAULT; + return 0; +} + +static long get_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv __user *p32) +{ + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(p64->recv_type, &p32->recv_type) || + __get_user(tmp, &p32->addr) || + __get_user(p64->addr_len, &p32->addr_len) || + __get_user(p64->msgid, &p32->msgid) || + get_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + p64->addr = compat_ptr(tmp); + return 0; +} + +static long put_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv __user *p32) +{ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + __put_user(p64->recv_type, &p32->recv_type) || + __put_user(p64->addr_len, &p32->addr_len) || + __put_user(p64->msgid, &p32->msgid) || + put_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + return 0; +} + +/* + * Handle compatibility ioctls + */ +static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc; + struct ipmi_file_private *priv = filep->private_data; + + switch(cmd) { + case COMPAT_IPMICTL_SEND_COMMAND: + { + struct ipmi_req rp; + + if (get_compat_ipmi_req(&rp, compat_ptr(arg))) + return -EFAULT; + + return handle_send_req(priv->user, &rp, + priv->default_retries, + priv->default_retry_time_ms); + } + case COMPAT_IPMICTL_SEND_COMMAND_SETTIME: + { + struct ipmi_req_settime sp; + + if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg))) + return -EFAULT; + + return handle_send_req(priv->user, &sp.req, + sp.retries, sp.retry_time_ms); + } + case COMPAT_IPMICTL_RECEIVE_MSG: + case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: + { + struct ipmi_recv *precv64, recv64; + + if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) + return -EFAULT; + + precv64 = compat_alloc_user_space(sizeof(recv64)); + if (copy_to_user(precv64, &recv64, sizeof(recv64))) + return -EFAULT; + + rc = ipmi_ioctl(filep->f_dentry->d_inode, filep, + ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) + ? IPMICTL_RECEIVE_MSG + : IPMICTL_RECEIVE_MSG_TRUNC), + (long) precv64); + if (rc != 0) + return rc; + + if (copy_from_user(&recv64, precv64, sizeof(recv64))) + return -EFAULT; + + if (put_compat_ipmi_recv(&recv64, compat_ptr(arg))) + return -EFAULT; + + return rc; + } + default: + return ipmi_ioctl(filep->f_dentry->d_inode, filep, cmd, arg); + } +} +#endif static struct file_operations ipmi_fops = { .owner = THIS_MODULE, .ioctl = ipmi_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_ipmi_ioctl, +#endif .open = ipmi_open, .release = ipmi_release, .fasync = ipmi_fasync, diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 0c81652eaba6..1813d0d198f1 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -54,7 +54,9 @@ static int ipmi_init_msghandler(void); static int initialized = 0; -static struct proc_dir_entry *proc_ipmi_root = NULL; +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *proc_ipmi_root = NULL; +#endif /* CONFIG_PROC_FS */ #define MAX_EVENTS_IN_QUEUE 25 @@ -124,11 +126,13 @@ struct ipmi_channel unsigned char protocol; }; +#ifdef CONFIG_PROC_FS struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; }; +#endif #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 8 @@ -156,10 +160,13 @@ struct ipmi_smi struct ipmi_smi_handlers *handlers; void *send_info; +#ifdef CONFIG_PROC_FS /* A list of proc entries for this interface. This does not need a lock, only one thread creates it and only one thread destroys it. */ + spinlock_t proc_entry_lock; struct ipmi_proc_entry *proc_entries; +#endif /* A table of sequence numbers for this interface. We use the sequence numbers for IPMB messages that go out of the @@ -1470,8 +1477,9 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, write_proc_t *write_proc, void *data, struct module *owner) { - struct proc_dir_entry *file; int rv = 0; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *file; struct ipmi_proc_entry *entry; /* Create a list element. */ @@ -1497,10 +1505,13 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, file->write_proc = write_proc; file->owner = owner; + spin_lock(&smi->proc_entry_lock); /* Stick it on the list. */ entry->next = smi->proc_entries; smi->proc_entries = entry; + spin_unlock(&smi->proc_entry_lock); } +#endif /* CONFIG_PROC_FS */ return rv; } @@ -1509,6 +1520,7 @@ static int add_proc_entries(ipmi_smi_t smi, int num) { int rv = 0; +#ifdef CONFIG_PROC_FS sprintf(smi->proc_dir_name, "%d", num); smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) @@ -1531,14 +1543,17 @@ static int add_proc_entries(ipmi_smi_t smi, int num) rv = ipmi_smi_add_proc_entry(smi, "version", version_file_read_proc, NULL, smi, THIS_MODULE); +#endif /* CONFIG_PROC_FS */ return rv; } static void remove_proc_entries(ipmi_smi_t smi) { +#ifdef CONFIG_PROC_FS struct ipmi_proc_entry *entry; + spin_lock(&smi->proc_entry_lock); while (smi->proc_entries) { entry = smi->proc_entries; smi->proc_entries = entry->next; @@ -1547,7 +1562,9 @@ static void remove_proc_entries(ipmi_smi_t smi) kfree(entry->name); kfree(entry); } + spin_unlock(&smi->proc_entry_lock); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); +#endif /* CONFIG_PROC_FS */ } static int @@ -1694,6 +1711,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, new_intf->seq_table[j].seqid = 0; } new_intf->curr_seq = 0; +#ifdef CONFIG_PROC_FS + spin_lock_init(&(new_intf->proc_entry_lock)); +#endif spin_lock_init(&(new_intf->waiting_msgs_lock)); INIT_LIST_HEAD(&(new_intf->waiting_msgs)); spin_lock_init(&(new_intf->events_lock)); @@ -2747,16 +2767,13 @@ static struct timer_list ipmi_timer; the queue and this silliness can go away. */ #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) -static volatile int stop_operation = 0; -static volatile int timer_stopped = 0; +static atomic_t stop_operation; static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME; static void ipmi_timeout(unsigned long data) { - if (stop_operation) { - timer_stopped = 1; + if (atomic_read(&stop_operation)) return; - } ticks_to_req_ev--; if (ticks_to_req_ev == 0) { @@ -2766,8 +2783,7 @@ static void ipmi_timeout(unsigned long data) ipmi_timeout_handler(IPMI_TIMEOUT_TIME); - ipmi_timer.expires += IPMI_TIMEOUT_JIFFIES; - add_timer(&ipmi_timer); + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } @@ -3089,6 +3105,7 @@ static int ipmi_init_msghandler(void) ipmi_interfaces[i] = NULL; } +#ifdef CONFIG_PROC_FS proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { printk(KERN_ERR PFX "Unable to create IPMI proc dir"); @@ -3096,6 +3113,7 @@ static int ipmi_init_msghandler(void) } proc_ipmi_root->owner = THIS_MODULE; +#endif /* CONFIG_PROC_FS */ init_timer(&ipmi_timer); ipmi_timer.data = 0; @@ -3130,13 +3148,12 @@ static __exit void cleanup_ipmi(void) /* Tell the timer to stop, then wait for it to stop. This avoids problems with race conditions removing the timer here. */ - stop_operation = 1; - while (!timer_stopped) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } + atomic_inc(&stop_operation); + del_timer_sync(&ipmi_timer); +#ifdef CONFIG_PROC_FS remove_proc_entry(proc_ipmi_root->name, &proc_root); +#endif /* CONFIG_PROC_FS */ initialized = 0; @@ -3177,4 +3194,5 @@ EXPORT_SYMBOL(ipmi_get_my_address); EXPORT_SYMBOL(ipmi_set_my_LUN); EXPORT_SYMBOL(ipmi_get_my_LUN); EXPORT_SYMBOL(ipmi_smi_add_proc_entry); +EXPORT_SYMBOL(proc_ipmi_root); EXPORT_SYMBOL(ipmi_user_set_run_to_completion); diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index cb5cdc6f14bf..f951c30236c9 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -31,10 +31,13 @@ * 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 @@ -44,6 +47,18 @@ /* Where to we insert our poweroff function? */ extern void (*pm_power_off)(void); +/* Definitions for controlling power off (if the system supports it). It + * conveniently matches the IPMI chassis control values. */ +#define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */ +#define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ + +/* the IPMI data command */ +static int poweroff_control = IPMI_CHASSIS_POWER_DOWN; + +/* parameter definition to allow user to flag power cycle */ +module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN); +MODULE_PARM_DESC(poweroff_control, " Set to 2 to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down."); + /* Stuff from the get device id command. */ static unsigned int mfg_id; static unsigned int prod_id; @@ -75,10 +90,10 @@ static struct ipmi_recv_msg halt_recv_msg = static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) { - struct semaphore *sem = recv_msg->user_msg_data; + struct completion *comp = recv_msg->user_msg_data; - if (sem) - up(sem); + if (comp) + complete(comp); } static struct ipmi_user_hndl ipmi_poweroff_handler = @@ -91,27 +106,27 @@ static int ipmi_request_wait_for_response(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { - int rv; - struct semaphore sem; + int rv; + struct completion comp; - sema_init (&sem, 0); + init_completion(&comp); - rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &comp, &halt_smi_msg, &halt_recv_msg, 0); if (rv) return rv; - down (&sem); + wait_for_completion(&comp); return halt_recv_msg.msg.data[0]; } -/* We are in run-to-completion mode, no semaphore is desired. */ +/* We are in run-to-completion mode, no completion is desired. */ static int ipmi_request_in_rc_mode(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { - int rv; + int rv; rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, &halt_smi_msg, &halt_recv_msg, 0); @@ -349,26 +364,38 @@ static void ipmi_poweroff_chassis (ipmi_user_t user) smi_addr.channel = IPMI_BMC_CHANNEL; smi_addr.lun = 0; - printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); + powercyclefailed: + printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", + ((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle")); /* * Power down */ send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; - data[0] = 0; /* Power down */ + data[0] = poweroff_control; send_msg.data = data; send_msg.data_len = sizeof(data); rv = ipmi_request_in_rc_mode(user, (struct ipmi_addr *) &smi_addr, &send_msg); if (rv) { - printk(KERN_ERR PFX "Unable to send chassis powerdown message," - " IPMI error 0x%x\n", rv); - goto out; + switch (poweroff_control) { + case IPMI_CHASSIS_POWER_CYCLE: + /* power cycle failed, default to power down */ + printk(KERN_ERR PFX "Unable to send chassis power " \ + "cycle message, IPMI error 0x%x\n", rv); + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + goto powercyclefailed; + + case IPMI_CHASSIS_POWER_DOWN: + default: + printk(KERN_ERR PFX "Unable to send chassis power " \ + "down message, IPMI error 0x%x\n", rv); + break; + } } - out: return; } @@ -430,7 +457,8 @@ static void ipmi_po_new_smi(int if_num) if (ready) return; - rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); + rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, + &ipmi_user); if (rv) { printk(KERN_ERR PFX "could not create IPMI user, error %d\n", rv); @@ -509,21 +537,84 @@ static struct ipmi_smi_watcher smi_watcher = }; +#ifdef CONFIG_PROC_FS +/* displays properties to proc */ +static int proc_read_chassctrl(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n", + poweroff_control); +} + +/* process property writes from proc */ +static int proc_write_chassctrl(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int rv = count; + unsigned int newval = 0; + + sscanf(buffer, "%d", &newval); + switch (newval) { + case IPMI_CHASSIS_POWER_CYCLE: + printk(KERN_INFO PFX "power cycle is now enabled\n"); + poweroff_control = newval; + break; + + case IPMI_CHASSIS_POWER_DOWN: + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + break; + + default: + rv = -EINVAL; + break; + } + + return rv; +} +#endif /* CONFIG_PROC_FS */ + /* * Startup and shutdown functions. */ static int ipmi_poweroff_init (void) { - int rv; + int rv; + struct proc_dir_entry *file; printk ("Copyright (C) 2004 MontaVista Software -" " IPMI Powerdown via sys_reboot version " IPMI_POWEROFF_VERSION ".\n"); - rv = ipmi_smi_watcher_register(&smi_watcher); - if (rv) - printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); + switch (poweroff_control) { + case IPMI_CHASSIS_POWER_CYCLE: + printk(KERN_INFO PFX "Power cycle is enabled.\n"); + break; + case IPMI_CHASSIS_POWER_DOWN: + default: + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + break; + } + + rv = ipmi_smi_watcher_register(&smi_watcher); + if (rv) { + printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); + goto out_err; + } + +#ifdef CONFIG_PROC_FS + file = create_proc_entry("poweroff_control", 0, proc_ipmi_root); + if (!file) { + printk(KERN_ERR PFX "Unable to create proc power control\n"); + } else { + file->nlink = 1; + file->read_proc = proc_read_chassctrl; + file->write_proc = proc_write_chassctrl; + file->owner = THIS_MODULE; + } +#endif + + out_err: return rv; } @@ -532,6 +623,10 @@ static __exit void ipmi_poweroff_cleanup(void) { int rv; +#ifdef CONFIG_PROC_FS + remove_proc_entry("poweroff_control", proc_ipmi_root); +#endif + ipmi_smi_watcher_unregister(&smi_watcher); if (ready) { diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index b3dbff1cf967..5079beda69b5 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -960,7 +960,7 @@ static char hdlc_unregister_fail[] __exitdata = static void __exit n_hdlc_exit(void) { /* Release tty registration of line discipline */ - int status = tty_register_ldisc(N_HDLC, NULL); + int status = tty_unregister_ldisc(N_HDLC); if (status) printk(hdlc_unregister_fail, status); diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 3883073ab48f..2291a87e8ada 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -200,7 +200,7 @@ static void __exit r3964_exit(void) TRACE_M ("cleanup_module()"); - status=tty_register_ldisc(N_R3964, NULL); + status=tty_unregister_ldisc(N_R3964); if(status!=0) { diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 8ce508b29865..5c843c9bf819 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -19,7 +19,7 @@ * * Note, the TPM chip is not interrupt driven (only polling) * and can have very long timeouts (minutes!). Hence the unusual - * calls to schedule_timeout. + * calls to msleep. * */ @@ -28,19 +28,16 @@ #include #include "tpm.h" -#define TPM_MINOR 224 /* officially assigned */ - -#define TPM_BUFSIZE 2048 - -/* PCI configuration addresses */ -#define PCI_GEN_PMCON_1 0xA0 -#define PCI_GEN1_DEC 0xE4 -#define PCI_LPC_EN 0xE6 -#define PCI_GEN2_DEC 0xEC +enum tpm_const { + TPM_MINOR = 224, /* officially assigned */ + TPM_BUFSIZE = 2048, + TPM_NUM_DEVICES = 256, + TPM_NUM_MASK_ENTRIES = TPM_NUM_DEVICES / (8 * sizeof(int)) +}; static LIST_HEAD(tpm_chip_list); static DEFINE_SPINLOCK(driver_lock); -static int dev_mask[32]; +static int dev_mask[TPM_NUM_MASK_ENTRIES]; static void user_reader_timeout(unsigned long ptr) { @@ -52,92 +49,17 @@ static void user_reader_timeout(unsigned long ptr) up(&chip->buffer_mutex); } -void tpm_time_expired(unsigned long ptr) -{ - int *exp = (int *) ptr; - *exp = 1; -} - -EXPORT_SYMBOL_GPL(tpm_time_expired); - -/* - * Initialize the LPC bus and enable the TPM ports - */ -int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base) -{ - u32 lpcenable, tmp; - int is_lpcm = 0; - - switch (pci_dev->vendor) { - case PCI_VENDOR_ID_INTEL: - switch (pci_dev->device) { - case PCI_DEVICE_ID_INTEL_82801CA_12: - case PCI_DEVICE_ID_INTEL_82801DB_12: - is_lpcm = 1; - break; - } - /* init ICH (enable LPC) */ - pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable); - lpcenable |= 0x20000000; - pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable); - - if (is_lpcm) { - pci_read_config_dword(pci_dev, PCI_GEN1_DEC, - &lpcenable); - if ((lpcenable & 0x20000000) == 0) { - dev_err(&pci_dev->dev, - "cannot enable LPC\n"); - return -ENODEV; - } - } - - /* initialize TPM registers */ - pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp); - - if (!is_lpcm) - tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0); - else - tmp = - (tmp & 0xFFFF0000) | (base & 0xFFF0) | - 0x00000001; - - pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp); - - if (is_lpcm) { - pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1, - &tmp); - tmp |= 0x00000004; /* enable CLKRUN */ - pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1, - tmp); - } - tpm_write_index(0x0D, 0x55); /* unlock 4F */ - tpm_write_index(0x0A, 0x00); /* int disable */ - tpm_write_index(0x08, base); /* base addr lo */ - tpm_write_index(0x09, (base & 0xFF00) >> 8); /* base addr hi */ - tpm_write_index(0x0D, 0xAA); /* lock 4F */ - break; - case PCI_VENDOR_ID_AMD: - /* nothing yet */ - break; - } - - return 0; -} - -EXPORT_SYMBOL_GPL(tpm_lpc_bus_init); - /* * Internal kernel interface to transmit TPM commands */ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz) { - ssize_t len; + ssize_t rc; u32 count; - __be32 *native_size; + unsigned long stop; - native_size = (__force __be32 *) (buf + 2); - count = be32_to_cpu(*native_size); + count = be32_to_cpu(*((__be32 *) (buf + 2))); if (count == 0) return -ENODATA; @@ -149,53 +71,49 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, down(&chip->tpm_mutex); - if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) { + if ((rc = chip->vendor->send(chip, (u8 *) buf, count)) < 0) { dev_err(&chip->pci_dev->dev, - "tpm_transmit: tpm_send: error %zd\n", len); - return len; + "tpm_transmit: tpm_send: error %zd\n", rc); + goto out; } - down(&chip->timer_manipulation_mutex); - chip->time_expired = 0; - init_timer(&chip->device_timer); - chip->device_timer.function = tpm_time_expired; - chip->device_timer.expires = jiffies + 2 * 60 * HZ; - chip->device_timer.data = (unsigned long) &chip->time_expired; - add_timer(&chip->device_timer); - up(&chip->timer_manipulation_mutex); - + stop = jiffies + 2 * 60 * HZ; do { u8 status = inb(chip->vendor->base + 1); if ((status & chip->vendor->req_complete_mask) == chip->vendor->req_complete_val) { - down(&chip->timer_manipulation_mutex); - del_singleshot_timer_sync(&chip->device_timer); - up(&chip->timer_manipulation_mutex); goto out_recv; } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(TPM_TIMEOUT); + + if ((status == chip->vendor->req_canceled)) { + dev_err(&chip->pci_dev->dev, "Operation Canceled\n"); + rc = -ECANCELED; + goto out; + } + + msleep(TPM_TIMEOUT); /* CHECK */ rmb(); - } while (!chip->time_expired); + } while (time_before(jiffies, stop)); chip->vendor->cancel(chip); - dev_err(&chip->pci_dev->dev, "Time expired\n"); - up(&chip->tpm_mutex); - return -EIO; + dev_err(&chip->pci_dev->dev, "Operation Timed out\n"); + rc = -ETIME; + goto out; out_recv: - len = chip->vendor->recv(chip, (u8 *) buf, bufsiz); - if (len < 0) + rc = chip->vendor->recv(chip, (u8 *) buf, bufsiz); + if (rc < 0) dev_err(&chip->pci_dev->dev, - "tpm_transmit: tpm_recv: error %zd\n", len); + "tpm_transmit: tpm_recv: error %zd\n", rc); +out: up(&chip->tpm_mutex); - return len; + return rc; } #define TPM_DIGEST_SIZE 20 #define CAP_PCR_RESULT_SIZE 18 -static u8 cap_pcr[] = { +static const u8 cap_pcr[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 22, /* length */ 0, 0, 0, 101, /* TPM_ORD_GetCapability */ @@ -205,75 +123,94 @@ static u8 cap_pcr[] = { }; #define READ_PCR_RESULT_SIZE 30 -static u8 pcrread[] = { +static const u8 pcrread[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 14, /* length */ 0, 0, 0, 21, /* TPM_ORD_PcrRead */ 0, 0, 0, 0 /* PCR index */ }; -static ssize_t show_pcrs(struct device *dev, struct device_attribute *attr, char *buf) +ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, + char *buf) { u8 data[READ_PCR_RESULT_SIZE]; ssize_t len; - int i, j, index, num_pcrs; + int i, j, num_pcrs; + __be32 index; char *str = buf; struct tpm_chip *chip = - pci_get_drvdata(container_of(dev, struct pci_dev, dev)); + pci_get_drvdata(to_pci_dev(dev)); if (chip == NULL) return -ENODEV; memcpy(data, cap_pcr, sizeof(cap_pcr)); if ((len = tpm_transmit(chip, data, sizeof(data))) - < CAP_PCR_RESULT_SIZE) - return len; + < CAP_PCR_RESULT_SIZE) { + dev_dbg(&chip->pci_dev->dev, "A TPM error (%d) occurred " + "attempting to determine the number of PCRS\n", + be32_to_cpu(*((__be32 *) (data + 6)))); + return 0; + } - num_pcrs = be32_to_cpu(*((__force __be32 *) (data + 14))); + num_pcrs = be32_to_cpu(*((__be32 *) (data + 14))); for (i = 0; i < num_pcrs; i++) { memcpy(data, pcrread, sizeof(pcrread)); index = cpu_to_be32(i); memcpy(data + 10, &index, 4); if ((len = tpm_transmit(chip, data, sizeof(data))) - < READ_PCR_RESULT_SIZE) - return len; + < READ_PCR_RESULT_SIZE){ + dev_dbg(&chip->pci_dev->dev, "A TPM error (%d) occurred" + " attempting to read PCR %d of %d\n", + be32_to_cpu(*((__be32 *) (data + 6))), i, num_pcrs); + goto out; + } str += sprintf(str, "PCR-%02d: ", i); for (j = 0; j < TPM_DIGEST_SIZE; j++) str += sprintf(str, "%02X ", *(data + 10 + j)); str += sprintf(str, "\n"); } +out: return str - buf; } - -static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL); +EXPORT_SYMBOL_GPL(tpm_show_pcrs); #define READ_PUBEK_RESULT_SIZE 314 -static u8 readpubek[] = { +static const u8 readpubek[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 30, /* length */ 0, 0, 0, 124, /* TPM_ORD_ReadPubek */ }; -static ssize_t show_pubek(struct device *dev, struct device_attribute *attr, char *buf) +ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, + char *buf) { - u8 data[READ_PUBEK_RESULT_SIZE]; + u8 *data; ssize_t len; - __be32 *native_val; - int i; + int i, rc; char *str = buf; struct tpm_chip *chip = - pci_get_drvdata(container_of(dev, struct pci_dev, dev)); + pci_get_drvdata(to_pci_dev(dev)); if (chip == NULL) return -ENODEV; + data = kmalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, readpubek, sizeof(readpubek)); memset(data + sizeof(readpubek), 0, 20); /* zero nonce */ - if ((len = tpm_transmit(chip, data, sizeof(data))) < - READ_PUBEK_RESULT_SIZE) - return len; + if ((len = tpm_transmit(chip, data, READ_PUBEK_RESULT_SIZE)) < + READ_PUBEK_RESULT_SIZE) { + dev_dbg(&chip->pci_dev->dev, "A TPM error (%d) occurred " + "attempting to read the PUBEK\n", + be32_to_cpu(*((__be32 *) (data + 6)))); + rc = 0; + goto out; + } /* ignore header 10 bytes @@ -286,8 +223,6 @@ static ssize_t show_pubek(struct device *dev, struct device_attribute *attr, cha ignore checksum 20 bytes */ - native_val = (__force __be32 *) (data + 34); - str += sprintf(str, "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n" @@ -298,21 +233,23 @@ static ssize_t show_pubek(struct device *dev, struct device_attribute *attr, cha data[15], data[16], data[17], data[22], data[23], data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], data[32], data[33], - be32_to_cpu(*native_val) - ); + be32_to_cpu(*((__be32 *) (data + 32)))); for (i = 0; i < 256; i++) { str += sprintf(str, "%02X ", data[i + 39]); if ((i + 1) % 16 == 0) str += sprintf(str, "\n"); } - return str - buf; + rc = str - buf; +out: + kfree(data); + return rc; } -static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL); +EXPORT_SYMBOL_GPL(tpm_show_pubek); #define CAP_VER_RESULT_SIZE 18 -static u8 cap_version[] = { +static const u8 cap_version[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 18, /* length */ 0, 0, 0, 101, /* TPM_ORD_GetCapability */ @@ -321,7 +258,7 @@ static u8 cap_version[] = { }; #define CAP_MANUFACTURER_RESULT_SIZE 18 -static u8 cap_manufacturer[] = { +static const u8 cap_manufacturer[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 22, /* length */ 0, 0, 0, 101, /* TPM_ORD_GetCapability */ @@ -330,14 +267,15 @@ static u8 cap_manufacturer[] = { 0, 0, 1, 3 }; -static ssize_t show_caps(struct device *dev, struct device_attribute *attr, char *buf) +ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, + char *buf) { - u8 data[READ_PUBEK_RESULT_SIZE]; + u8 data[sizeof(cap_manufacturer)]; ssize_t len; char *str = buf; struct tpm_chip *chip = - pci_get_drvdata(container_of(dev, struct pci_dev, dev)); + pci_get_drvdata(to_pci_dev(dev)); if (chip == NULL) return -ENODEV; @@ -348,7 +286,7 @@ static ssize_t show_caps(struct device *dev, struct device_attribute *attr, char return len; str += sprintf(str, "Manufacturer: 0x%x\n", - be32_to_cpu(*(data + 14))); + be32_to_cpu(*((__be32 *) (data + 14)))); memcpy(data, cap_version, sizeof(cap_version)); @@ -363,8 +301,20 @@ static ssize_t show_caps(struct device *dev, struct device_attribute *attr, char return str - buf; } +EXPORT_SYMBOL_GPL(tpm_show_caps); + +ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return 0; + + chip->vendor->cancel(chip); + return count; +} +EXPORT_SYMBOL_GPL(tpm_store_cancel); -static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL); /* * Device file system interface to the TPM @@ -422,24 +372,15 @@ EXPORT_SYMBOL_GPL(tpm_open); int tpm_release(struct inode *inode, struct file *file) { struct tpm_chip *chip = file->private_data; - - file->private_data = NULL; spin_lock(&driver_lock); + file->private_data = NULL; chip->num_opens--; - spin_unlock(&driver_lock); - - down(&chip->timer_manipulation_mutex); - if (timer_pending(&chip->user_read_timer)) - del_singleshot_timer_sync(&chip->user_read_timer); - else if (timer_pending(&chip->device_timer)) - del_singleshot_timer_sync(&chip->device_timer); - up(&chip->timer_manipulation_mutex); - - kfree(chip->data_buffer); + del_singleshot_timer_sync(&chip->user_read_timer); atomic_set(&chip->data_pending, 0); - pci_dev_put(chip->pci_dev); + kfree(chip->data_buffer); + spin_unlock(&driver_lock); return 0; } @@ -453,10 +394,8 @@ ssize_t tpm_write(struct file * file, const char __user * buf, /* cannot perform a write until the read has cleared either via tpm_read or a user_read_timer timeout */ - while (atomic_read(&chip->data_pending) != 0) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(TPM_TIMEOUT); - } + while (atomic_read(&chip->data_pending) != 0) + msleep(TPM_TIMEOUT); down(&chip->buffer_mutex); @@ -476,13 +415,7 @@ ssize_t tpm_write(struct file * file, const char __user * buf, up(&chip->buffer_mutex); /* Set a timeout by which the reader must come claim the result */ - down(&chip->timer_manipulation_mutex); - init_timer(&chip->user_read_timer); - chip->user_read_timer.function = user_reader_timeout; - chip->user_read_timer.data = (unsigned long) chip; - chip->user_read_timer.expires = jiffies + (60 * HZ); - add_timer(&chip->user_read_timer); - up(&chip->timer_manipulation_mutex); + mod_timer(&chip->user_read_timer, jiffies + (60 * HZ)); return in_size; } @@ -493,29 +426,19 @@ ssize_t tpm_read(struct file * file, char __user * buf, size_t size, loff_t * off) { struct tpm_chip *chip = file->private_data; - int ret_size = -ENODATA; + int ret_size; - if (atomic_read(&chip->data_pending) != 0) { /* Result available */ - down(&chip->timer_manipulation_mutex); - del_singleshot_timer_sync(&chip->user_read_timer); - up(&chip->timer_manipulation_mutex); + del_singleshot_timer_sync(&chip->user_read_timer); + ret_size = atomic_read(&chip->data_pending); + atomic_set(&chip->data_pending, 0); + if (ret_size > 0) { /* relay data */ + if (size < ret_size) + ret_size = size; down(&chip->buffer_mutex); - - ret_size = atomic_read(&chip->data_pending); - atomic_set(&chip->data_pending, 0); - - if (ret_size == 0) /* timeout just occurred */ - ret_size = -ETIME; - else if (ret_size > 0) { /* relay data */ - if (size < ret_size) - ret_size = size; - - if (copy_to_user((void __user *) buf, - chip->data_buffer, ret_size)) { - ret_size = -EFAULT; - } - } + if (copy_to_user + ((void __user *) buf, chip->data_buffer, ret_size)) + ret_size = -EFAULT; up(&chip->buffer_mutex); } @@ -542,13 +465,11 @@ void __devexit tpm_remove(struct pci_dev *pci_dev) pci_set_drvdata(pci_dev, NULL); misc_deregister(&chip->vendor->miscdev); - device_remove_file(&pci_dev->dev, &dev_attr_pubek); - device_remove_file(&pci_dev->dev, &dev_attr_pcrs); - device_remove_file(&pci_dev->dev, &dev_attr_caps); + sysfs_remove_group(&pci_dev->dev.kobj, chip->vendor->attr_group); pci_disable_device(pci_dev); - dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32)); + dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &= !(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES)); kfree(chip); @@ -590,10 +511,6 @@ int tpm_pm_resume(struct pci_dev *pci_dev) if (chip == NULL) return -ENODEV; - spin_lock(&driver_lock); - tpm_lpc_bus_init(pci_dev, chip->vendor->base); - spin_unlock(&driver_lock); - return 0; } @@ -622,17 +539,21 @@ int tpm_register_hardware(struct pci_dev *pci_dev, init_MUTEX(&chip->buffer_mutex); init_MUTEX(&chip->tpm_mutex); - init_MUTEX(&chip->timer_manipulation_mutex); INIT_LIST_HEAD(&chip->list); + init_timer(&chip->user_read_timer); + chip->user_read_timer.function = user_reader_timeout; + chip->user_read_timer.data = (unsigned long) chip; + chip->vendor = entry; chip->dev_num = -1; - for (i = 0; i < 32; i++) - for (j = 0; j < 8; j++) + for (i = 0; i < TPM_NUM_MASK_ENTRIES; i++) + for (j = 0; j < 8 * sizeof(int); j++) if ((dev_mask[i] & (1 << j)) == 0) { - chip->dev_num = i * 32 + j; + chip->dev_num = + i * TPM_NUM_MASK_ENTRIES + j; dev_mask[i] |= 1 << j; goto dev_num_search_complete; } @@ -665,32 +586,21 @@ dev_num_search_complete: return -ENODEV; } + spin_lock(&driver_lock); + pci_set_drvdata(pci_dev, chip); list_add(&chip->list, &tpm_chip_list); - device_create_file(&pci_dev->dev, &dev_attr_pubek); - device_create_file(&pci_dev->dev, &dev_attr_pcrs); - device_create_file(&pci_dev->dev, &dev_attr_caps); + spin_unlock(&driver_lock); + + sysfs_create_group(&pci_dev->dev.kobj, chip->vendor->attr_group); return 0; } EXPORT_SYMBOL_GPL(tpm_register_hardware); -static int __init init_tpm(void) -{ - return 0; -} - -static void __exit cleanup_tpm(void) -{ - -} - -module_init(init_tpm); -module_exit(cleanup_tpm); - MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); MODULE_DESCRIPTION("TPM Driver"); MODULE_VERSION("2.0"); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index de0c796fce80..10cb450191a6 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -25,23 +25,38 @@ #include #include -#define TPM_TIMEOUT msecs_to_jiffies(5) +enum tpm_timeout { + TPM_TIMEOUT = 5, /* msecs */ +}; /* TPM addresses */ -#define TPM_ADDR 0x4E -#define TPM_DATA 0x4F +enum tpm_addr { + TPM_ADDR = 0x4E, + TPM_DATA = 0x4F +}; + +extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr, + const char *, size_t); struct tpm_chip; struct tpm_vendor_specific { u8 req_complete_mask; u8 req_complete_val; + u8 req_canceled; u16 base; /* TPM base address */ int (*recv) (struct tpm_chip *, u8 *, size_t); int (*send) (struct tpm_chip *, u8 *, size_t); void (*cancel) (struct tpm_chip *); struct miscdevice miscdev; + struct attribute_group *attr_group; }; struct tpm_chip { @@ -58,8 +73,6 @@ struct tpm_chip { struct timer_list user_read_timer; /* user needs to claim result */ struct semaphore tpm_mutex; /* tpm is processing */ - struct timer_list device_timer; /* tpm is processing */ - struct semaphore timer_manipulation_mutex; struct tpm_vendor_specific *vendor; @@ -78,9 +91,6 @@ static inline void tpm_write_index(int index, int value) outb(value & 0xFF, TPM_DATA); } -extern void tpm_time_expired(unsigned long); -extern int tpm_lpc_bus_init(struct pci_dev *, u16); - extern int tpm_register_hardware(struct pci_dev *, struct tpm_vendor_specific *); extern int tpm_open(struct inode *, struct file *); diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index f9333e729b62..61fe14a77124 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -22,17 +22,23 @@ #include "tpm.h" /* Atmel definitions */ -#define TPM_ATML_BASE 0x400 +enum tpm_atmel_addr { + TPM_ATMEL_BASE_ADDR_LO = 0x08, + TPM_ATMEL_BASE_ADDR_HI = 0x09 +}; /* write status bits */ -#define ATML_STATUS_ABORT 0x01 -#define ATML_STATUS_LASTBYTE 0x04 - +enum tpm_atmel_write_status { + ATML_STATUS_ABORT = 0x01, + ATML_STATUS_LASTBYTE = 0x04 +}; /* read status bits */ -#define ATML_STATUS_BUSY 0x01 -#define ATML_STATUS_DATA_AVAIL 0x02 -#define ATML_STATUS_REWRITE 0x04 - +enum tpm_atmel_read_status { + ATML_STATUS_BUSY = 0x01, + ATML_STATUS_DATA_AVAIL = 0x02, + ATML_STATUS_REWRITE = 0x04, + ATML_STATUS_READY = 0x08 +}; static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count) { @@ -121,13 +127,29 @@ static struct file_operations atmel_ops = { .release = tpm_release, }; +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); +static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel); + +static struct attribute* atmel_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + 0, +}; + +static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs }; + static struct tpm_vendor_specific tpm_atmel = { .recv = tpm_atml_recv, .send = tpm_atml_send, .cancel = tpm_atml_cancel, .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, .req_complete_val = ATML_STATUS_DATA_AVAIL, - .base = TPM_ATML_BASE, + .req_canceled = ATML_STATUS_READY, + .attr_group = &atmel_attr_grp, .miscdev = { .fops = &atmel_ops, }, }; @@ -136,14 +158,16 @@ static int __devinit tpm_atml_init(struct pci_dev *pci_dev, { u8 version[4]; int rc = 0; + int lo, hi; if (pci_enable_device(pci_dev)) return -EIO; - if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) { - rc = -ENODEV; - goto out_err; - } + lo = tpm_read_index( TPM_ATMEL_BASE_ADDR_LO ); + hi = tpm_read_index( TPM_ATMEL_BASE_ADDR_HI ); + + tpm_atmel.base = (hi<<8)|lo; + dev_dbg( &pci_dev->dev, "Operating with base: 0x%x\n", tpm_atmel.base); /* verify that it is an Atmel part */ if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T' @@ -183,6 +207,7 @@ static struct pci_device_id tpm_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)}, {PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)}, + {PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6LPC)}, {0,} }; diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 9cce833a0923..1a45e7dfc13b 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -22,43 +22,52 @@ #include "tpm.h" /* National definitions */ -#define TPM_NSC_BASE 0x360 -#define TPM_NSC_IRQ 0x07 +enum tpm_nsc_addr{ + TPM_NSC_BASE = 0x360, + TPM_NSC_IRQ = 0x07, + TPM_NSC_BASE0_HI = 0x60, + TPM_NSC_BASE0_LO = 0x61, + TPM_NSC_BASE1_HI = 0x62, + TPM_NSC_BASE1_LO = 0x63 +}; -#define NSC_LDN_INDEX 0x07 -#define NSC_SID_INDEX 0x20 -#define NSC_LDC_INDEX 0x30 -#define NSC_DIO_INDEX 0x60 -#define NSC_CIO_INDEX 0x62 -#define NSC_IRQ_INDEX 0x70 -#define NSC_ITS_INDEX 0x71 +enum tpm_nsc_index { + NSC_LDN_INDEX = 0x07, + NSC_SID_INDEX = 0x20, + NSC_LDC_INDEX = 0x30, + NSC_DIO_INDEX = 0x60, + NSC_CIO_INDEX = 0x62, + NSC_IRQ_INDEX = 0x70, + NSC_ITS_INDEX = 0x71 +}; -#define NSC_STATUS 0x01 -#define NSC_COMMAND 0x01 -#define NSC_DATA 0x00 +enum tpm_nsc_status_loc { + NSC_STATUS = 0x01, + NSC_COMMAND = 0x01, + NSC_DATA = 0x00 +}; /* status bits */ -#define NSC_STATUS_OBF 0x01 /* output buffer full */ -#define NSC_STATUS_IBF 0x02 /* input buffer full */ -#define NSC_STATUS_F0 0x04 /* F0 */ -#define NSC_STATUS_A2 0x08 /* A2 */ -#define NSC_STATUS_RDY 0x10 /* ready to receive command */ -#define NSC_STATUS_IBR 0x20 /* ready to receive data */ - +enum tpm_nsc_status { + NSC_STATUS_OBF = 0x01, /* output buffer full */ + NSC_STATUS_IBF = 0x02, /* input buffer full */ + NSC_STATUS_F0 = 0x04, /* F0 */ + NSC_STATUS_A2 = 0x08, /* A2 */ + NSC_STATUS_RDY = 0x10, /* ready to receive command */ + NSC_STATUS_IBR = 0x20 /* ready to receive data */ +}; /* command bits */ -#define NSC_COMMAND_NORMAL 0x01 /* normal mode */ -#define NSC_COMMAND_EOC 0x03 -#define NSC_COMMAND_CANCEL 0x22 - +enum tpm_nsc_cmd_mode { + NSC_COMMAND_NORMAL = 0x01, /* normal mode */ + NSC_COMMAND_EOC = 0x03, + NSC_COMMAND_CANCEL = 0x22 +}; /* * Wait for a certain status to appear */ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) { - int expired = 0; - struct timer_list status_timer = - TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ, - (unsigned long) &expired); + unsigned long stop; /* status immediately available check */ *data = inb(chip->vendor->base + NSC_STATUS); @@ -66,17 +75,14 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) return 0; /* wait for status */ - add_timer(&status_timer); + stop = jiffies + 10 * HZ; do { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(TPM_TIMEOUT); + msleep(TPM_TIMEOUT); *data = inb(chip->vendor->base + 1); - if ((*data & mask) == val) { - del_singleshot_timer_sync(&status_timer); + if ((*data & mask) == val) return 0; - } } - while (!expired); + while (time_before(jiffies, stop)); return -EBUSY; } @@ -84,10 +90,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) static int nsc_wait_for_ready(struct tpm_chip *chip) { int status; - int expired = 0; - struct timer_list status_timer = - TIMER_INITIALIZER(tpm_time_expired, jiffies + 100, - (unsigned long) &expired); + unsigned long stop; /* status immediately available check */ status = inb(chip->vendor->base + NSC_STATUS); @@ -97,19 +100,16 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) return 0; /* wait for status */ - add_timer(&status_timer); + stop = jiffies + 100; do { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(TPM_TIMEOUT); + msleep(TPM_TIMEOUT); status = inb(chip->vendor->base + NSC_STATUS); if (status & NSC_STATUS_OBF) status = inb(chip->vendor->base + NSC_DATA); - if (status & NSC_STATUS_RDY) { - del_singleshot_timer_sync(&status_timer); + if (status & NSC_STATUS_RDY) return 0; - } } - while (!expired); + while (time_before(jiffies, stop)); dev_info(&chip->pci_dev->dev, "wait for ready failed\n"); return -EBUSY; @@ -228,30 +228,46 @@ static struct file_operations nsc_ops = { .release = tpm_release, }; +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); +static DEVICE_ATTR(cancel, S_IWUSR|S_IWGRP, NULL, tpm_store_cancel); + +static struct attribute * nsc_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + 0, +}; + +static struct attribute_group nsc_attr_grp = { .attrs = nsc_attrs }; + static struct tpm_vendor_specific tpm_nsc = { .recv = tpm_nsc_recv, .send = tpm_nsc_send, .cancel = tpm_nsc_cancel, .req_complete_mask = NSC_STATUS_OBF, .req_complete_val = NSC_STATUS_OBF, - .base = TPM_NSC_BASE, + .req_canceled = NSC_STATUS_RDY, + .attr_group = &nsc_attr_grp, .miscdev = { .fops = &nsc_ops, }, - }; static int __devinit tpm_nsc_init(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { int rc = 0; + int lo, hi; + + hi = tpm_read_index(TPM_NSC_BASE0_HI); + lo = tpm_read_index(TPM_NSC_BASE0_LO); + + tpm_nsc.base = (hi<<8) | lo; if (pci_enable_device(pci_dev)) return -EIO; - if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) { - rc = -ENODEV; - goto out_err; - } - /* verify that it is a National part (SID) */ if (tpm_read_index(NSC_SID_INDEX) != 0xEF) { rc = -ENODEV; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 31831030f73f..cc4b43bad703 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -251,7 +251,7 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) static DEFINE_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); -static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */ +static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) { @@ -262,24 +262,35 @@ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) return -EINVAL; spin_lock_irqsave(&tty_ldisc_lock, flags); - if (new_ldisc) { - tty_ldiscs[disc] = *new_ldisc; - tty_ldiscs[disc].num = disc; - tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED; - tty_ldiscs[disc].refcount = 0; - } else { - if(tty_ldiscs[disc].refcount) - ret = -EBUSY; - else - tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED; - } + tty_ldiscs[disc] = *new_ldisc; + tty_ldiscs[disc].num = disc; + tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED; + tty_ldiscs[disc].refcount = 0; spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } - EXPORT_SYMBOL(tty_register_ldisc); +int tty_unregister_ldisc(int disc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty_ldiscs[disc].refcount) + ret = -EBUSY; + else + tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_unregister_ldisc); + struct tty_ldisc *tty_ldisc_get(int disc) { unsigned long flags; diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index 06a31da2381c..b53e2e2b5aee 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -414,6 +414,16 @@ config WATCHDOG_RIO machines. The watchdog timeout period is normally one minute but can be changed with a boot-time parameter. +# ppc64 RTAS watchdog +config WATCHDOG_RTAS + tristate "RTAS watchdog" + depends on WATCHDOG && PPC_RTAS + help + This driver adds watchdog support for the RTAS watchdog. + + To compile this driver as a module, choose M here. The module + will be called wdrtas. + # # ISA-based Watchdog Cards # diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 1cd27efa35c1..c1838834ea7f 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o +obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o # Only one watchdog can succeed. We probe the hardware watchdog # drivers first, then the softdog driver. This means if your hardware diff --git a/drivers/char/watchdog/wdrtas.c b/drivers/char/watchdog/wdrtas.c new file mode 100644 index 000000000000..619e2ffca33f --- /dev/null +++ b/drivers/char/watchdog/wdrtas.c @@ -0,0 +1,696 @@ +/* + * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as + * RTAS calls are available + */ + +/* + * RTAS watchdog driver + * + * (C) Copyright IBM Corp. 2005 + * device driver to exploit watchdog RTAS functions + * + * Authors : Utz Bacher + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define WDRTAS_MAGIC_CHAR 42 +#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \ + WDIOF_MAGICCLOSE) + +MODULE_AUTHOR("Utz Bacher "); +MODULE_DESCRIPTION("RTAS watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS_MISCDEV(TEMP_MINOR); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int wdrtas_nowayout = 1; +#else +static int wdrtas_nowayout = 0; +#endif + +static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0); +static char wdrtas_expect_close = 0; + +static int wdrtas_interval; + +#define WDRTAS_THERMAL_SENSOR 3 +static int wdrtas_token_get_sensor_state; +#define WDRTAS_SURVEILLANCE_IND 9000 +static int wdrtas_token_set_indicator; +#define WDRTAS_SP_SPI 28 +static int wdrtas_token_get_sp; +static int wdrtas_token_event_scan; + +#define WDRTAS_DEFAULT_INTERVAL 300 + +#define WDRTAS_LOGBUFFER_LEN 128 +static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN]; + + +/*** watchdog access functions */ + +/** + * wdrtas_set_interval - sets the watchdog interval + * @interval: new interval + * + * returns 0 on success, <0 on failures + * + * wdrtas_set_interval sets the watchdog keepalive interval by calling the + * RTAS function set-indicator (surveillance). The unit of interval is + * seconds. + */ +static int +wdrtas_set_interval(int interval) +{ + long result; + static int print_msg = 10; + + /* rtas uses minutes */ + interval = (interval + 59) / 60; + + result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL, + WDRTAS_SURVEILLANCE_IND, 0, interval); + if ( (result < 0) && (print_msg) ) { + printk(KERN_ERR "wdrtas: setting the watchdog to %i " + "timeout failed: %li\n", interval, result); + print_msg--; + } + + return result; +} + +/** + * wdrtas_get_interval - returns the current watchdog interval + * @fallback_value: value (in seconds) to use, if the RTAS call fails + * + * returns the interval + * + * wdrtas_get_interval returns the current watchdog keepalive interval + * as reported by the RTAS function ibm,get-system-parameter. The unit + * of the return value is seconds. + */ +static int +wdrtas_get_interval(int fallback_value) +{ + long result; + char value[4]; + + result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL, + WDRTAS_SP_SPI, (void *)__pa(&value), 4); + if ( (value[0] != 0) || (value[1] != 2) || (value[3] != 0) || + (result < 0) ) { + printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog " + "timeout (%li). Continuing\n", result); + return fallback_value; + } + + /* rtas uses minutes */ + return ((int)value[2]) * 60; +} + +/** + * wdrtas_timer_start - starts watchdog + * + * wdrtas_timer_start starts the watchdog by calling the RTAS function + * set-interval (surveillance) + */ +static void +wdrtas_timer_start(void) +{ + wdrtas_set_interval(wdrtas_interval); +} + +/** + * wdrtas_timer_stop - stops watchdog + * + * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function + * set-interval (surveillance) + */ +static void +wdrtas_timer_stop(void) +{ + wdrtas_set_interval(0); +} + +/** + * wdrtas_log_scanned_event - logs an event we received during keepalive + * + * wdrtas_log_scanned_event prints a message to the log buffer dumping + * the results of the last event-scan call + */ +static void +wdrtas_log_scanned_event(void) +{ + int i; + + for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16) + printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16), + wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1], + wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3], + wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5], + wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7], + wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9], + wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11], + wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13], + wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]); +} + +/** + * wdrtas_timer_keepalive - resets watchdog timer to keep system alive + * + * wdrtas_timer_keepalive restarts the watchdog timer by calling the + * RTAS function event-scan and repeats these calls as long as there are + * events available. All events will be dumped. + */ +static void +wdrtas_timer_keepalive(void) +{ + long result; + + do { + result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL, + RTAS_EVENT_SCAN_ALL_EVENTS, 0, + (void *)__pa(wdrtas_logbuffer), + WDRTAS_LOGBUFFER_LEN); + if (result < 0) + printk(KERN_ERR "wdrtas: event-scan failed: %li\n", + result); + if (result == 0) + wdrtas_log_scanned_event(); + } while (result == 0); +} + +/** + * wdrtas_get_temperature - returns current temperature + * + * returns temperature or <0 on failures + * + * wdrtas_get_temperature returns the current temperature in Fahrenheit. It + * uses the RTAS call get-sensor-state, token 3 to do so + */ +static int +wdrtas_get_temperature(void) +{ + long result; + int temperature = 0; + + result = rtas_call(wdrtas_token_get_sensor_state, 2, 2, + (void *)__pa(&temperature), + WDRTAS_THERMAL_SENSOR, 0); + + if (result < 0) + printk(KERN_WARNING "wdrtas: reading the thermal sensor " + "faild: %li\n", result); + else + temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */ + + return temperature; +} + +/** + * wdrtas_get_status - returns the status of the watchdog + * + * returns a bitmask of defines WDIOF_... as defined in + * include/linux/watchdog.h + */ +static int +wdrtas_get_status(void) +{ + return 0; /* TODO */ +} + +/** + * wdrtas_get_boot_status - returns the reason for the last boot + * + * returns a bitmask of defines WDIOF_... as defined in + * include/linux/watchdog.h, indicating why the watchdog rebooted the system + */ +static int +wdrtas_get_boot_status(void) +{ + return 0; /* TODO */ +} + +/*** watchdog API and operations stuff */ + +/* wdrtas_write - called when watchdog device is written to + * @file: file structure + * @buf: user buffer with data + * @len: amount to data written + * @ppos: position in file + * + * returns the number of successfully processed characters, which is always + * the number of bytes passed to this function + * + * wdrtas_write processes all the data given to it and looks for the magic + * character 'V'. This character allows the watchdog device to be closed + * properly. + */ +static ssize_t +wdrtas_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + int i; + char c; + + if (!len) + goto out; + + if (!wdrtas_nowayout) { + wdrtas_expect_close = 0; + /* look for 'V' */ + for (i = 0; i < len; i++) { + if (get_user(c, buf + i)) + return -EFAULT; + /* allow to close device */ + if (c == 'V') + wdrtas_expect_close = WDRTAS_MAGIC_CHAR; + } + } + + wdrtas_timer_keepalive(); + +out: + return len; +} + +/** + * wdrtas_ioctl - ioctl function for the watchdog device + * @inode: inode structure + * @file: file structure + * @cmd: command for ioctl + * @arg: argument pointer + * + * returns 0 on success, <0 on failure + * + * wdrtas_ioctl implements the watchdog API ioctls + */ +static int +wdrtas_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int __user *argp = (void *)arg; + int i; + static struct watchdog_info wdinfo = { + .options = WDRTAS_SUPPORTED_MASK, + .firmware_version = 0, + .identity = "wdrtas" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &wdinfo, sizeof(wdinfo))) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + i = wdrtas_get_status(); + return put_user(i, argp); + + case WDIOC_GETBOOTSTATUS: + i = wdrtas_get_boot_status(); + return put_user(i, argp); + + case WDIOC_GETTEMP: + if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) + return -EOPNOTSUPP; + + i = wdrtas_get_temperature(); + return put_user(i, argp); + + case WDIOC_SETOPTIONS: + if (get_user(i, argp)) + return -EFAULT; + if (i & WDIOS_DISABLECARD) + wdrtas_timer_stop(); + if (i & WDIOS_ENABLECARD) { + wdrtas_timer_keepalive(); + wdrtas_timer_start(); + } + if (i & WDIOS_TEMPPANIC) { + /* not implemented. Done by H8 */ + } + return 0; + + case WDIOC_KEEPALIVE: + wdrtas_timer_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(i, argp)) + return -EFAULT; + + if (wdrtas_set_interval(i)) + return -EINVAL; + + wdrtas_timer_keepalive(); + + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) + wdrtas_interval = i; + else + wdrtas_interval = wdrtas_get_interval(i); + /* fallthrough */ + + case WDIOC_GETTIMEOUT: + return put_user(wdrtas_interval, argp); + + default: + return -ENOIOCTLCMD; + } +} + +/** + * wdrtas_open - open function of watchdog device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success, -EBUSY if the file has been opened already, <0 on + * other failures + * + * function called when watchdog device is opened + */ +static int +wdrtas_open(struct inode *inode, struct file *file) +{ + /* only open once */ + if (atomic_inc_return(&wdrtas_miscdev_open) > 1) { + atomic_dec(&wdrtas_miscdev_open); + return -EBUSY; + } + + wdrtas_timer_start(); + wdrtas_timer_keepalive(); + + return nonseekable_open(inode, file); +} + +/** + * wdrtas_close - close function of watchdog device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success + * + * close function. Always succeeds + */ +static int +wdrtas_close(struct inode *inode, struct file *file) +{ + /* only stop watchdog, if this was announced using 'V' before */ + if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR) + wdrtas_timer_stop(); + else { + printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog " + "not stopped.\n"); + wdrtas_timer_keepalive(); + } + + wdrtas_expect_close = 0; + atomic_dec(&wdrtas_miscdev_open); + return 0; +} + +/** + * wdrtas_temp_read - gives back the temperature in fahrenheit + * @file: file structure + * @buf: user buffer + * @count: number of bytes to be read + * @ppos: position in file + * + * returns always 1 or -EFAULT in case of user space copy failures, <0 on + * other failures + * + * wdrtas_temp_read gives the temperature to the users by copying this + * value as one byte into the user space buffer. The unit is Fahrenheit... + */ +static ssize_t +wdrtas_temp_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int temperature = 0; + + temperature = wdrtas_get_temperature(); + if (temperature < 0) + return temperature; + + if (copy_to_user(buf, &temperature, 1)) + return -EFAULT; + + return 1; +} + +/** + * wdrtas_temp_open - open function of temperature device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success, <0 on failure + * + * function called when temperature device is opened + */ +static int +wdrtas_temp_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +/** + * wdrtas_temp_close - close function of temperature device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success + * + * close function. Always succeeds + */ +static int +wdrtas_temp_close(struct inode *inode, struct file *file) +{ + return 0; +} + +/** + * wdrtas_reboot - reboot notifier function + * @nb: notifier block structure + * @code: reboot code + * @ptr: unused + * + * returns NOTIFY_DONE + * + * wdrtas_reboot stops the watchdog in case of a reboot + */ +static int +wdrtas_reboot(struct notifier_block *this, unsigned long code, void *ptr) +{ + if ( (code==SYS_DOWN) || (code==SYS_HALT) ) + wdrtas_timer_stop(); + + return NOTIFY_DONE; +} + +/*** initialization stuff */ + +static struct file_operations wdrtas_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdrtas_write, + .ioctl = wdrtas_ioctl, + .open = wdrtas_open, + .release = wdrtas_close, +}; + +static struct miscdevice wdrtas_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdrtas_fops, +}; + +static struct file_operations wdrtas_temp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = wdrtas_temp_read, + .open = wdrtas_temp_open, + .release = wdrtas_temp_close, +}; + +static struct miscdevice wdrtas_tempdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &wdrtas_temp_fops, +}; + +static struct notifier_block wdrtas_notifier = { + .notifier_call = wdrtas_reboot, +}; + +/** + * wdrtas_get_tokens - reads in RTAS tokens + * + * returns 0 on succes, <0 on failure + * + * wdrtas_get_tokens reads in the tokens for the RTAS calls used in + * this watchdog driver. It tolerates, if "get-sensor-state" and + * "ibm,get-system-parameter" are not available. + */ +static int +wdrtas_get_tokens(void) +{ + wdrtas_token_get_sensor_state = rtas_token("get-sensor-state"); + if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) { + printk(KERN_WARNING "wdrtas: couldn't get token for " + "get-sensor-state. Trying to continue without " + "temperature support.\n"); + } + + wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter"); + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) { + printk(KERN_WARNING "wdrtas: couldn't get token for " + "ibm,get-system-parameter. Trying to continue with " + "a default timeout value of %i seconds.\n", + WDRTAS_DEFAULT_INTERVAL); + } + + wdrtas_token_set_indicator = rtas_token("set-indicator"); + if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) { + printk(KERN_ERR "wdrtas: couldn't get token for " + "set-indicator. Terminating watchdog code.\n"); + return -EIO; + } + + wdrtas_token_event_scan = rtas_token("event-scan"); + if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) { + printk(KERN_ERR "wdrtas: couldn't get token for event-scan. " + "Terminating watchdog code.\n"); + return -EIO; + } + + return 0; +} + +/** + * wdrtas_unregister_devs - unregisters the misc dev handlers + * + * wdrtas_register_devs unregisters the watchdog and temperature watchdog + * misc devs + */ +static void +wdrtas_unregister_devs(void) +{ + misc_deregister(&wdrtas_miscdev); + if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) + misc_deregister(&wdrtas_tempdev); +} + +/** + * wdrtas_register_devs - registers the misc dev handlers + * + * returns 0 on succes, <0 on failure + * + * wdrtas_register_devs registers the watchdog and temperature watchdog + * misc devs + */ +static int +wdrtas_register_devs(void) +{ + int result; + + result = misc_register(&wdrtas_miscdev); + if (result) { + printk(KERN_ERR "wdrtas: couldn't register watchdog misc " + "device. Terminating watchdog code.\n"); + return result; + } + + if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) { + result = misc_register(&wdrtas_tempdev); + if (result) { + printk(KERN_WARNING "wdrtas: couldn't register " + "watchdog temperature misc device. Continuing " + "without temperature support.\n"); + wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE; + } + } + + return 0; +} + +/** + * wdrtas_init - init function of the watchdog driver + * + * returns 0 on succes, <0 on failure + * + * registers the file handlers and the reboot notifier + */ +static int __init +wdrtas_init(void) +{ + if (wdrtas_get_tokens()) + return -ENODEV; + + if (wdrtas_register_devs()) + return -ENODEV; + + if (register_reboot_notifier(&wdrtas_notifier)) { + printk(KERN_ERR "wdrtas: could not register reboot notifier. " + "Terminating watchdog code.\n"); + wdrtas_unregister_devs(); + return -ENODEV; + } + + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) + wdrtas_interval = WDRTAS_DEFAULT_INTERVAL; + else + wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL); + + return 0; +} + +/** + * wdrtas_exit - exit function of the watchdog driver + * + * unregisters the file handlers and the reboot notifier + */ +static void __exit +wdrtas_exit(void) +{ + if (!wdrtas_nowayout) + wdrtas_timer_stop(); + + wdrtas_unregister_devs(); + + unregister_reboot_notifier(&wdrtas_notifier); +} + +module_init(wdrtas_init); +module_exit(wdrtas_exit); diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index df1b721154d2..839b44a7e08b 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -23,12 +23,15 @@ setup_serial_console(struct pcdp_uart *uart) { #ifdef CONFIG_SERIAL_8250_CONSOLE int mmio; - static char options[64]; + static char options[64], *p = options; mmio = (uart->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY); - snprintf(options, sizeof(options), "console=uart,%s,0x%lx,%lun%d", - mmio ? "mmio" : "io", uart->addr.address, uart->baud, - uart->bits ? uart->bits : 8); + p += sprintf(p, "console=uart,%s,0x%lx", + mmio ? "mmio" : "io", uart->addr.address); + if (uart->baud) + p += sprintf(p, ",%lu", uart->baud); + if (uart->bits) + p += sprintf(p, "n%d", uart->bits); return early_serial_console_init(options); #else diff --git a/drivers/firmware/pcdp.h b/drivers/firmware/pcdp.h index 863bb6f768c3..1dc7c88b7b4d 100644 --- a/drivers/firmware/pcdp.h +++ b/drivers/firmware/pcdp.h @@ -2,7 +2,7 @@ * Definitions for PCDP-defined console devices * * v1.0a: http://www.dig64.org/specifications/DIG64_HCDPv10a_01.pdf - * v2.0: http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf + * v2.0: http://www.dig64.org/specifications/DIG64_PCDPv20.pdf * * (c) Copyright 2002, 2004 Hewlett-Packard Development Company, L.P. * Khalid Aziz diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c index ec943cad2314..1956af382cd8 100644 --- a/drivers/i2c/busses/i2c-ixp2000.c +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -33,7 +33,8 @@ #include #include -#include /* Pick up IXP42000-specific bits */ +#include /* Pick up IXP2000-specific bits */ +#include static inline int ixp2000_scl_pin(void *data) { diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 39f3e9101ed4..0a31cfda08a0 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -2656,17 +2656,64 @@ int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock) return cdrom_lockdoor(drive, lock, NULL); } +static +int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_page *cap) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct packet_command cgc; + int stat, attempts = 3, size = sizeof(*cap); + + /* + * ACER50 (and others?) require the full spec length mode sense + * page capabilities size, but older drives break. + */ + if (!(!strcmp(drive->id->model, "ATAPI CD ROM DRIVE 50X MAX") || + !strcmp(drive->id->model, "WPI CDS-32X"))) + size -= sizeof(cap->pad); + + init_cdrom_command(&cgc, cap, size, CGC_DATA_UNKNOWN); + do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ + stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (!stat) + break; + } while (--attempts); + return stat; +} + +static +void ide_cdrom_update_speed (ide_drive_t *drive, struct atapi_capabilities_page *cap) +{ + /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ + if (!drive->id->model[0] && + !strncmp(drive->id->fw_rev, "241N", 4)) { + CDROM_STATE_FLAGS(drive)->current_speed = + (((unsigned int)cap->curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS(drive)->max_speed = + (((unsigned int)cap->maxspeed) + (176/2)) / 176; + } else { + CDROM_STATE_FLAGS(drive)->current_speed = + (ntohs(cap->curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS(drive)->max_speed = + (ntohs(cap->maxspeed) + (176/2)) / 176; + } +} + static int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed) { ide_drive_t *drive = (ide_drive_t*) cdi->handle; struct request_sense sense; + struct atapi_capabilities_page cap; int stat; if ((stat = cdrom_select_speed(drive, speed, &sense)) < 0) return stat; - cdi->speed = CDROM_STATE_FLAGS(drive)->current_speed; + if (!ide_cdrom_get_capabilities(drive, &cap)) { + ide_cdrom_update_speed(drive, &cap); + cdi->speed = CDROM_STATE_FLAGS(drive)->current_speed; + } return 0; } @@ -2868,31 +2915,6 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots) return register_cdrom(devinfo); } -static -int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_page *cap) -{ - struct cdrom_info *info = drive->driver_data; - struct cdrom_device_info *cdi = &info->devinfo; - struct packet_command cgc; - int stat, attempts = 3, size = sizeof(*cap); - - /* - * ACER50 (and others?) require the full spec length mode sense - * page capabilities size, but older drives break. - */ - if (!(!strcmp(drive->id->model, "ATAPI CD ROM DRIVE 50X MAX") || - !strcmp(drive->id->model, "WPI CDS-32X"))) - size -= sizeof(cap->pad); - - init_cdrom_command(&cgc, cap, size, CGC_DATA_UNKNOWN); - do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ - stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); - if (!stat) - break; - } while (--attempts); - return stat; -} - static int ide_cdrom_probe_capabilities (ide_drive_t *drive) { @@ -2978,20 +3000,7 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) } } - /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ - if (!drive->id->model[0] && - !strncmp(drive->id->fw_rev, "241N", 4)) { - CDROM_STATE_FLAGS(drive)->current_speed = - (((unsigned int)cap.curspeed) + (176/2)) / 176; - CDROM_CONFIG_FLAGS(drive)->max_speed = - (((unsigned int)cap.maxspeed) + (176/2)) / 176; - } else { - CDROM_STATE_FLAGS(drive)->current_speed = - (ntohs(cap.curspeed) + (176/2)) / 176; - CDROM_CONFIG_FLAGS(drive)->max_speed = - (ntohs(cap.maxspeed) + (176/2)) / 176; - } - + ide_cdrom_update_speed(drive, &cap); /* don't print speed if the drive reported 0. */ printk(KERN_INFO "%s: ATAPI", drive->name); diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 3302cd8eab4c..d6f934886b04 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -1215,7 +1215,8 @@ static int ide_disk_probe(struct device *dev) if (!idkp) goto failed; - g = alloc_disk(1 << PARTN_BITS); + g = alloc_disk_node(1 << PARTN_BITS, + pcibus_to_node(drive->hwif->pci_dev->bus)); if (!g) goto out_free_idkp; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index c949e98df4b6..9eab6426148e 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -661,10 +661,12 @@ static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, un idefloppy_do_end_request(drive, 1, done >> 9); +#if IDEFLOPPY_DEBUG_BUGS if (bcount) { printk(KERN_ERR "%s: leftover data in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount); idefloppy_write_zeros(drive, bcount); } +#endif } static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc) @@ -1048,6 +1050,9 @@ static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *p atapi_bcount_t bcount; ide_handler_t *pkt_xfer_routine; +#if 0 /* Accessing floppy->pc is not valid here, the previous pc may be gone + and have lived on another thread's stack; that stack may have become + unmapped meanwhile (CONFIG_DEBUG_PAGEALLOC). */ #if IDEFLOPPY_DEBUG_BUGS if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { @@ -1055,6 +1060,7 @@ static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *p "Two request sense in serial were issued\n"); } #endif /* IDEFLOPPY_DEBUG_BUGS */ +#endif if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 5d876f53c697..7df85af75371 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -977,8 +977,9 @@ static int ide_init_queue(ide_drive_t *drive) * limits and LBA48 we could raise it but as yet * do not. */ - - q = blk_init_queue(do_ide_request, &ide_lock); + + q = blk_init_queue_node(do_ide_request, &ide_lock, + pcibus_to_node(drive->hwif->pci_dev->bus)); if (!q) return 1; @@ -1095,7 +1096,8 @@ static int init_irq (ide_hwif_t *hwif) hwgroup->hwif->next = hwif; spin_unlock_irq(&ide_lock); } else { - hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL); + hwgroup = kmalloc_node(sizeof(ide_hwgroup_t), GFP_KERNEL, + pcibus_to_node(hwif->drives[0].hwif->pci_dev->bus)); if (!hwgroup) goto out_up; diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index f6b85222ba3d..79ca38469159 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -257,7 +257,7 @@ static int __init serport_init(void) static void __exit serport_exit(void) { - tty_register_ldisc(N_MOUSE, NULL); + tty_unregister_ldisc(N_MOUSE); } module_init(serport_init); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index ee3c869d9701..200a0688f717 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -122,14 +122,6 @@ static struct hash_cell *__get_uuid_cell(const char *str) /*----------------------------------------------------------------- * Inserting, removing and renaming a device. *---------------------------------------------------------------*/ -static inline char *kstrdup(const char *str) -{ - char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); - if (r) - strcpy(r, str); - return r; -} - static struct hash_cell *alloc_cell(const char *name, const char *uuid, struct mapped_device *md) { @@ -139,7 +131,7 @@ static struct hash_cell *alloc_cell(const char *name, const char *uuid, if (!hc) return NULL; - hc->name = kstrdup(name); + hc->name = kstrdup(name, GFP_KERNEL); if (!hc->name) { kfree(hc); return NULL; @@ -149,7 +141,7 @@ static struct hash_cell *alloc_cell(const char *name, const char *uuid, hc->uuid = NULL; else { - hc->uuid = kstrdup(uuid); + hc->uuid = kstrdup(uuid, GFP_KERNEL); if (!hc->uuid) { kfree(hc->name); kfree(hc); @@ -273,7 +265,7 @@ static int dm_hash_rename(const char *old, const char *new) /* * duplicate new. */ - new_name = kstrdup(new); + new_name = kstrdup(new, GFP_KERNEL); if (!new_name) return -ENOMEM; diff --git a/drivers/media/common/ir-common.c b/drivers/media/common/ir-common.c index 84a49d2ec919..4adb2843f8be 100644 --- a/drivers/media/common/ir-common.c +++ b/drivers/media/common/ir-common.c @@ -1,5 +1,5 @@ /* - * $Id: ir-common.c,v 1.8 2005/02/22 12:28:40 kraxel Exp $ + * $Id: ir-common.c,v 1.10 2005/05/22 19:23:39 nsh Exp $ * * some common structs and functions to handle infrared remotes via * input layer ... @@ -131,10 +131,10 @@ IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = { [ 18 ] = KEY_KP0, [ 0 ] = KEY_POWER, - [ 27 ] = KEY_LANGUAGE, //MTS button +// [ 27 ] = MTS button [ 2 ] = KEY_TUNER, // TV/FM [ 30 ] = KEY_VIDEO, - [ 22 ] = KEY_INFO, //display button +// [ 22 ] = display button [ 4 ] = KEY_VOLUMEUP, [ 8 ] = KEY_VOLUMEDOWN, [ 12 ] = KEY_CHANNELUP, @@ -142,7 +142,7 @@ IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = { [ 3 ] = KEY_ZOOM, // fullscreen [ 31 ] = KEY_SUBTITLE, // closed caption/teletext [ 32 ] = KEY_SLEEP, - [ 41 ] = KEY_SEARCH, //boss key +// [ 41 ] = boss key [ 20 ] = KEY_MUTE, [ 43 ] = KEY_RED, [ 44 ] = KEY_GREEN, @@ -150,17 +150,17 @@ IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = { [ 46 ] = KEY_BLUE, [ 24 ] = KEY_KPPLUS, //fine tune + [ 25 ] = KEY_KPMINUS, //fine tune - - [ 42 ] = KEY_ANGLE, //picture in picture - [ 33 ] = KEY_KPDOT, +// [ 42 ] = picture in picture + [ 33 ] = KEY_KPDOT, [ 19 ] = KEY_KPENTER, - [ 17 ] = KEY_AGAIN, //recall +// [ 17 ] = recall [ 34 ] = KEY_BACK, [ 35 ] = KEY_PLAYPAUSE, [ 36 ] = KEY_NEXT, - [ 37 ] = KEY_T, //time shifting +// [ 37 ] = time shifting [ 38 ] = KEY_STOP, - [ 39 ] = KEY_RECORD, - [ 40 ] = KEY_SHUFFLE //snapshot + [ 39 ] = KEY_RECORD +// [ 40 ] = snapshot }; EXPORT_SYMBOL_GPL(ir_codes_winfast); @@ -184,18 +184,30 @@ IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = { [ 0x07 ] = KEY_KP7, // 7 [ 0x08 ] = KEY_KP8, // 8 [ 0x09 ] = KEY_KP9, // 9 + [ 0x0a ] = KEY_TEXT, // keypad asterisk as well [ 0x0b ] = KEY_RED, // red button - [ 0x0c ] = KEY_OPTION, // black key without text + [ 0x0c ] = KEY_RADIO, // radio [ 0x0d ] = KEY_MENU, // menu + [ 0x0e ] = KEY_SUBTITLE, // also the # key [ 0x0f ] = KEY_MUTE, // mute [ 0x10 ] = KEY_VOLUMEUP, // volume + [ 0x11 ] = KEY_VOLUMEDOWN, // volume - - [ 0x1e ] = KEY_NEXT, // skip >| + [ 0x12 ] = KEY_PREVIOUS, // previous channel + [ 0x14 ] = KEY_UP, // up + [ 0x15 ] = KEY_DOWN, // down + [ 0x16 ] = KEY_LEFT, // left + [ 0x17 ] = KEY_RIGHT, // right + [ 0x18 ] = KEY_VIDEO, // Videos + [ 0x19 ] = KEY_AUDIO, // Music + [ 0x1a ] = KEY_MHP, // Pictures - presume this means "Multimedia Home Platform"- no "PICTURES" key in input.h + [ 0x1b ] = KEY_EPG, // Guide + [ 0x1c ] = KEY_TV, // TV + [ 0x1e ] = KEY_NEXTSONG, // skip >| [ 0x1f ] = KEY_EXIT, // back/exit [ 0x20 ] = KEY_CHANNELUP, // channel / program + [ 0x21 ] = KEY_CHANNELDOWN, // channel / program - [ 0x22 ] = KEY_CHANNEL, // source (old black remote) - [ 0x24 ] = KEY_PREVIOUS, // replay |< + [ 0x24 ] = KEY_PREVIOUSSONG, // replay |< [ 0x25 ] = KEY_ENTER, // OK [ 0x26 ] = KEY_SLEEP, // minimize (old black remote) [ 0x29 ] = KEY_BLUE, // blue key @@ -213,6 +225,39 @@ IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL(ir_codes_hauppauge_new); +IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE] = { + [ 2 ] = KEY_KP0, + [ 1 ] = KEY_KP1, + [ 11 ] = KEY_KP2, + [ 27 ] = KEY_KP3, + [ 5 ] = KEY_KP4, + [ 9 ] = KEY_KP5, + [ 21 ] = KEY_KP6, + [ 6 ] = KEY_KP7, + [ 10 ] = KEY_KP8, + [ 18 ] = KEY_KP9, + + [ 3 ] = KEY_TUNER, // TV/FM + [ 7 ] = KEY_SEARCH, // scan + [ 28 ] = KEY_ZOOM, // full screen + [ 30 ] = KEY_POWER, + [ 23 ] = KEY_VOLUMEDOWN, + [ 31 ] = KEY_VOLUMEUP, + [ 20 ] = KEY_CHANNELDOWN, + [ 22 ] = KEY_CHANNELUP, + [ 24 ] = KEY_MUTE, + + [ 0 ] = KEY_LIST, // source + [ 19 ] = KEY_INFO, // loop + [ 16 ] = KEY_LAST, // +100 + [ 13 ] = KEY_CLEAR, // reset + [ 12 ] = BTN_RIGHT, // fun++ + [ 4 ] = BTN_LEFT, // fun-- + [ 14 ] = KEY_GOTO, // function + [ 15 ] = KEY_STOP, // freeze +}; +EXPORT_SYMBOL(ir_codes_pixelview); + /* -------------------------------------------------------------------------- */ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) @@ -379,3 +424,4 @@ EXPORT_SYMBOL_GPL(ir_decode_biphase); * c-basic-offset: 8 * End: */ + diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index cb826c9adfe7..c04fd11526e0 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -403,7 +403,7 @@ static struct file_operations video_fops = .llseek = no_llseek, }; -void vv_callback(struct saa7146_dev *dev, unsigned long status) +static void vv_callback(struct saa7146_dev *dev, unsigned long status) { u32 isr = status; diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 4983e1b1bb1d..01387f883cdf 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -27,9 +27,9 @@ source "drivers/media/dvb/ttpci/Kconfig" comment "Supported USB Adapters" depends on DVB_CORE && USB +source "drivers/media/dvb/dvb-usb/Kconfig" source "drivers/media/dvb/ttusb-budget/Kconfig" source "drivers/media/dvb/ttusb-dec/Kconfig" -source "drivers/media/dvb/dibusb/Kconfig" source "drivers/media/dvb/cinergyT2/Kconfig" comment "Supported FlexCopII (B2C2) Adapters" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index 520fc3902819..3c6ff1619103 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -2,4 +2,4 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dibusb/ cinergyT2/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index 99bd675df955..fafd0ab3a28f 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -6,6 +6,7 @@ config DVB_B2C2_FLEXCOP select DVB_MT312 select DVB_NXT2002 select DVB_STV0297 + select DVB_BCM3510 help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 71be400e9aeb..0410cc96a48e 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -10,6 +10,7 @@ #include "stv0299.h" #include "mt352.h" #include "nxt2002.h" +#include "bcm3510.h" #include "stv0297.h" #include "mt312.h" @@ -285,21 +286,25 @@ static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_front } static struct mt352_config samsung_tdtc9251dh0_config = { - .demod_address = 0x0f, - .demod_init = samsung_tdtc9251dh0_demod_init, - .pll_set = samsung_tdtc9251dh0_pll_set, + .demod_init = samsung_tdtc9251dh0_demod_init, + .pll_set = samsung_tdtc9251dh0_pll_set, }; -static int nxt2002_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) { struct flexcop_device *fc = fe->dvb->priv; return request_firmware(fw, name, fc->dev); } static struct nxt2002_config samsung_tbmv_config = { - .demod_address = 0x0a, - .request_firmware = nxt2002_request_firmware, + .demod_address = 0x0a, + .request_firmware = flexcop_fe_request_firmware, +}; + +static struct bcm3510_config air2pc_atsc_first_gen_config = { + .demod_address = 0x0f, + .request_firmware = flexcop_fe_request_firmware, }; static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) @@ -354,11 +359,16 @@ int flexcop_frontend_init(struct flexcop_device *fc) fc->dev_type = FC_AIR_DVB; info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address); } else - /* try the air atsc (nxt2002) */ + /* try the air atsc 2nd generation (nxt2002) */ if ((fc->fe = nxt2002_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) { - fc->dev_type = FC_AIR_ATSC; + fc->dev_type = FC_AIR_ATSC2; info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address); } else + /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ + if ((fc->fe = bcm3510_attach(&air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) { + fc->dev_type = FC_AIR_ATSC1; + info("found the bcm3510 at i2c address: 0x%02x",air2pc_atsc_first_gen_config.demod_address); + } else /* try the cable dvb (stv0297) */ if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap, 0xf8)) != NULL) { fc->dev_type = FC_CABLE; diff --git a/drivers/media/dvb/b2c2/flexcop-misc.c b/drivers/media/dvb/b2c2/flexcop-misc.c index 19e06da46774..23082545651f 100644 --- a/drivers/media/dvb/b2c2/flexcop-misc.c +++ b/drivers/media/dvb/b2c2/flexcop-misc.c @@ -45,11 +45,12 @@ const char *flexcop_revision_names[] = { const char *flexcop_device_names[] = { "Unkown device", - "AirStar 2 DVB-T", - "AirStar 2 ATSC", - "SkyStar 2 DVB-S", - "SkyStar 2 DVB-S (old version)", - "CableStar 2 DVB-C", + "Air2PC/AirStar 2 DVB-T", + "Air2PC/AirStar 2 ATSC 1st generation", + "Air2PC/AirStar 2 ATSC 2nd generation", + "Sky2PC/SkyStar 2 DVB-S", + "Sky2PC/SkyStar 2 DVB-S (old version)", + "Cable2PC/CableStar 2 DVB-C", }; const char *flexcop_bus_names[] = { diff --git a/drivers/media/dvb/b2c2/flexcop-reg.h b/drivers/media/dvb/b2c2/flexcop-reg.h index 5e131be55cb3..75b50f21afe6 100644 --- a/drivers/media/dvb/b2c2/flexcop-reg.h +++ b/drivers/media/dvb/b2c2/flexcop-reg.h @@ -21,7 +21,8 @@ extern const char *flexcop_revision_names[]; typedef enum { FC_UNK = 0, FC_AIR_DVB, - FC_AIR_ATSC, + FC_AIR_ATSC1, + FC_AIR_ATSC2, FC_SKY, FC_SKY_OLD, FC_CABLE, diff --git a/drivers/media/dvb/dibusb/Kconfig b/drivers/media/dvb/dibusb/Kconfig deleted file mode 100644 index 74dfc73ae5b0..000000000000 --- a/drivers/media/dvb/dibusb/Kconfig +++ /dev/null @@ -1,62 +0,0 @@ -config DVB_DIBUSB - tristate "DiBcom USB DVB-T devices (see help for a complete device list)" - depends on DVB_CORE && USB - select FW_LOADER - select DVB_DIB3000MB - select DVB_DIB3000MC - select DVB_MT352 - help - Support for USB 1.1 and 2.0 DVB-T devices based on reference designs made by - DiBcom (http://www.dibcom.fr) and C&E. - - Devices supported by this driver: - - TwinhanDTV USB-Ter (VP7041) - TwinhanDTV Magic Box (VP7041e) - KWorld/JetWay/ADSTech V-Stream XPERT DTV - DVB-T USB1.1 and USB2.0 - Hama DVB-T USB-Box - DiBcom reference devices (non-public) - Ultima Electronic/Artec T1 USB TVBOX - Compro Videomate DVB-U2000 - DVB-T USB - Grandtec DVB-T USB - Avermedia AverTV DVBT USB - Artec T1 USB1.1 and USB2.0 boxes - Yakumo/Typhoon DVB-T USB2.0 - Hanftek UMT-010 USB2.0 - Hauppauge WinTV NOVA-T USB2 - - The VP7041 seems to be identical to "CTS Portable" (Chinese - Television System). - - These devices can be understood as budget ones, they "only" deliver - (a part of) the MPEG2 transport stream. - - A firmware is needed to get the device working. See Documentation/dvb/README.dibusb - details. - - Say Y if you own such a device and want to use it. You should build it as - a module. - -config DVB_DIBUSB_MISDESIGNED_DEVICES - bool "Enable support for some misdesigned (see help) devices, which identify with wrong IDs" - depends on DVB_DIBUSB - help - Somehow Artec/Ultima Electronic forgot to program the eeprom of some of their - USB1.1/USB2.0 devices. - So comes that they identify with the default Vendor and Product ID of the Cypress - CY7C64613 (AN2235) or Cypress FX2. - - Affected device IDs: - 0x0574:0x2235 (Artec T1 USB1.1, cold) - 0x04b4:0x8613 (Artec T1 USB2.0, cold) - 0x0574:0x1002 (Artec T1 USB2.0, warm) - 0x0574:0x2131 (aged DiBcom USB1.1 test device) - - Say Y if your device has one of the mentioned IDs. - -config DVB_DIBCOM_DEBUG - bool "Enable extended debug support for DiBcom USB device" - depends on DVB_DIBUSB - help - Say Y if you want to enable debuging. See modinfo dvb-dibusb for - debug levels. diff --git a/drivers/media/dvb/dibusb/Makefile b/drivers/media/dvb/dibusb/Makefile deleted file mode 100644 index e941c508624e..000000000000 --- a/drivers/media/dvb/dibusb/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -dvb-dibusb-objs = dvb-dibusb-core.o \ - dvb-dibusb-dvb.o \ - dvb-dibusb-fe-i2c.o \ - dvb-dibusb-firmware.o \ - dvb-dibusb-remote.o \ - dvb-dibusb-usb.o \ - dvb-fe-dtt200u.o - -obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o - -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-core.c b/drivers/media/dvb/dibusb/dvb-dibusb-core.c deleted file mode 100644 index 26235f9247e4..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb-core.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Driver for mobile USB Budget DVB-T devices based on reference - * design made by DiBcom (http://www.dibcom.fr/) - * - * dvb-dibusb-core.c - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * based on GPL code from DiBcom, which has - * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) - * - * Remote control code added by David Matthews (dm@prolingua.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, version 2. - * - * Acknowledgements - * - * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver - * sources, on which this driver (and the dib3000mb/mc/p frontends) are based. - * - * see Documentation/dvb/README.dibusb for more information - */ -#include "dvb-dibusb.h" - -#include - -/* debug */ -int dvb_dibusb_debug; -module_param_named(debug, dvb_dibusb_debug, int, 0644); - -#ifdef CONFIG_DVB_DIBCOM_DEBUG -#define DBSTATUS "" -#else -#define DBSTATUS " (debugging is not enabled)" -#endif -MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))." DBSTATUS); -#undef DBSTATUS - -static int pid_parse; -module_param(pid_parse, int, 0644); -MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0"); - -static int rc_query_interval = 100; -module_param(rc_query_interval, int, 0644); -MODULE_PARM_DESC(rc_query_interval, "interval in msecs for remote control query (default: 100; min: 40)"); - -static int rc_key_repeat_count = 2; -module_param(rc_key_repeat_count, int, 0644); -MODULE_PARM_DESC(rc_key_repeat_count, "how many key repeats will be dropped before passing the key event again (default: 2)"); - -/* Vendor IDs */ -#define USB_VID_ADSTECH 0x06e1 -#define USB_VID_ANCHOR 0x0547 -#define USB_VID_AVERMEDIA 0x14aa -#define USB_VID_COMPRO 0x185b -#define USB_VID_COMPRO_UNK 0x145f -#define USB_VID_CYPRESS 0x04b4 -#define USB_VID_DIBCOM 0x10b8 -#define USB_VID_EMPIA 0xeb1a -#define USB_VID_GRANDTEC 0x5032 -#define USB_VID_HANFTEK 0x15f4 -#define USB_VID_HAUPPAUGE 0x2040 -#define USB_VID_HYPER_PALTEK 0x1025 -#define USB_VID_IMC_NETWORKS 0x13d3 -#define USB_VID_TWINHAN 0x1822 -#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 - -/* Product IDs */ -#define USB_PID_ADSTECH_USB2_COLD 0xa333 -#define USB_PID_ADSTECH_USB2_WARM 0xa334 -#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 -#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 -#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 -#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 -#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c -#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d -#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 -#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 -#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 -#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 -#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 -#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 -#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 -#define USB_PID_KWORLD_VSTREAM_COLD 0x17de -#define USB_PID_KWORLD_VSTREAM_WARM 0x17df -#define USB_PID_TWINHAN_VP7041_COLD 0x3201 -#define USB_PID_TWINHAN_VP7041_WARM 0x3202 -#define USB_PID_ULTIMA_TVBOX_COLD 0x8105 -#define USB_PID_ULTIMA_TVBOX_WARM 0x8106 -#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 -#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 -#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 -#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 -#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 -#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 -#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e -#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f -#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 -#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 -#define USB_PID_YAKUMO_DTT200U_COLD 0x0201 -#define USB_PID_YAKUMO_DTT200U_WARM 0x0301 -#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 -#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 - -/* USB Driver stuff - * table of devices that this driver is working with - * - * ATTENTION: Never ever change the order of this table, the particular - * devices depend on this order - * - * Each entry is used as a reference in the device_struct. Currently this is - * the only non-redundant way of assigning USB ids to actual devices I'm aware - * of, because there is only one place in the code where the assignment of - * vendor and product id is done, here. - */ -static struct usb_device_id dib_table [] = { -/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_COLD)}, -/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_WARM)}, -/* 02 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_COLD) }, -/* 03 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_WARM) }, - -/* 04 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) }, -/* 05 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) }, -/* 06 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) }, -/* 07 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) }, -/* 08 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) }, -/* 09 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, -/* 10 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, -/* 11 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) }, -/* 12 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) }, -/* 13 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) }, -/* 14 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) }, -/* 15 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) }, -/* 16 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) }, -/* 17 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) }, -/* 18 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) }, -/* 19 */ { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_COLD) }, -/* 20 */ { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_WARM) }, -/* 21 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) }, -/* 22 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) }, -/* 23 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) }, -/* 24 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) }, -/* 25 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) }, -/* 26 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) }, -/* 27 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, - -/* 28 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, -/* 29 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, - -/* 30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) }, -/* 31 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) }, -/* 32 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) }, -/* 33 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) }, -/* - * activate the following define when you have one of the devices and want to - * build it from build-2.6 in dvb-kernel - */ -// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES -#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES -/* 34 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) }, -/* 35 */ { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) }, -/* 36 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) }, -/* 37 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_DIBCOM_ANCHOR_2135_COLD) }, -#endif - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, dib_table); - -static struct dibusb_usb_controller dibusb_usb_ctrl[] = { - { .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 }, - { .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 }, - { .name = "Cypress FX2", .cpu_cs_register = 0xe600 }, -}; - -struct dibusb_tuner dibusb_tuner[] = { - { DIBUSB_TUNER_CABLE_THOMSON, - 0x61 - }, - { DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5, - 0x60 - }, - { DIBUSB_TUNER_CABLE_LG_TDTP_E102P, - 0x61 - }, - { DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5, - 0x60 - }, -}; - -static struct dibusb_demod dibusb_demod[] = { - { DIBUSB_DIB3000MB, - 16, - { 0x8, 0 }, - }, - { DIBUSB_DIB3000MC, - 32, - { 0x9, 0xa, 0xb, 0xc }, - }, - { DIBUSB_MT352, - 254, - { 0xf, 0 }, - }, - { DTT200U_FE, - 8, - { 0xff,0 }, /* there is no i2c bus in this device */ - } -}; - -static struct dibusb_device_class dibusb_device_classes[] = { - { .id = DIBUSB1_1, .usb_ctrl = &dibusb_usb_ctrl[0], - .firmware = "dvb-dibusb-5.0.0.11.fw", - .pipe_cmd = 0x01, .pipe_data = 0x02, - .urb_count = 7, .urb_buffer_size = 4096, - DIBUSB_RC_NEC_PROTOCOL, - &dibusb_demod[DIBUSB_DIB3000MB], - &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON], - }, - { DIBUSB1_1_AN2235, &dibusb_usb_ctrl[1], - "dvb-dibusb-an2235-1.fw", - 0x01, 0x02, - 7, 4096, - DIBUSB_RC_NEC_PROTOCOL, - &dibusb_demod[DIBUSB_DIB3000MB], - &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON], - }, - { DIBUSB2_0,&dibusb_usb_ctrl[2], - "dvb-dibusb-6.0.0.5.fw", - 0x01, 0x06, - 7, 4096, - DIBUSB_RC_NEC_PROTOCOL, - &dibusb_demod[DIBUSB_DIB3000MC], - &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5], - }, - { UMT2_0, &dibusb_usb_ctrl[2], - "dvb-dibusb-umt-2.fw", - 0x01, 0x06, - 20, 512, - DIBUSB_RC_NO, - &dibusb_demod[DIBUSB_MT352], - &dibusb_tuner[DIBUSB_TUNER_CABLE_LG_TDTP_E102P], - }, - { DIBUSB2_0B,&dibusb_usb_ctrl[2], - "dvb-dibusb-adstech-usb2-1.fw", - 0x01, 0x06, - 7, 4096, - DIBUSB_RC_NEC_PROTOCOL, - &dibusb_demod[DIBUSB_DIB3000MB], - &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON], - }, - { NOVAT_USB2,&dibusb_usb_ctrl[2], - "dvb-dibusb-nova-t-1.fw", - 0x01, 0x06, - 7, 4096, - DIBUSB_RC_HAUPPAUGE_PROTO, - &dibusb_demod[DIBUSB_DIB3000MC], - &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5], - }, - { DTT200U,&dibusb_usb_ctrl[2], - "dvb-dtt200u-1.fw", - 0x01, 0x02, - 7, 4096, - DIBUSB_RC_NO, - &dibusb_demod[DTT200U_FE], - NULL, /* no explicit tuner/pll-programming necessary (it has the ENV57H1XD5) */ - }, -}; - -static struct dibusb_usb_device dibusb_devices[] = { - { "TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[19], &dib_table[21], NULL}, - { &dib_table[20], &dib_table[22], NULL}, - }, - { "KWorld V-Stream XPERT DTV - DVB-T USB1.1", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[11], NULL }, - { &dib_table[12], NULL }, - }, - { "Grandtec USB1.1 DVB-T", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[13], &dib_table[15], NULL }, - { &dib_table[14], &dib_table[16], NULL }, - }, - { "DiBcom USB1.1 DVB-T reference design (MOD3000)", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[7], NULL }, - { &dib_table[8], NULL }, - }, - { "Artec T1 USB1.1 TVBOX with AN2135", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[23], NULL }, - { &dib_table[24], NULL }, - }, - { "Artec T1 USB1.1 TVBOX with AN2235", - &dibusb_device_classes[DIBUSB1_1_AN2235], - { &dib_table[25], NULL }, - { &dib_table[26], NULL }, - }, - { "Avermedia AverTV DVBT USB1.1", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[0], NULL }, - { &dib_table[1], NULL }, - }, - { "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[4], &dib_table[6], NULL}, - { &dib_table[5], NULL }, - }, - { "Unkown USB1.1 DVB-T device ???? please report the name to the author", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[17], NULL }, - { &dib_table[18], NULL }, - }, - { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", - &dibusb_device_classes[DIBUSB2_0], - { &dib_table[9], NULL }, - { &dib_table[10], NULL }, - }, - { "Artec T1 USB2.0 TVBOX (please report the warm ID)", - &dibusb_device_classes[DIBUSB2_0], - { &dib_table[27], NULL }, - { NULL }, - }, - { "Hauppauge WinTV NOVA-T USB2", - &dibusb_device_classes[NOVAT_USB2], - { &dib_table[30], NULL }, - { &dib_table[31], NULL }, - }, - { "DTT200U (Yakumo/Hama/Typhoon) DVB-T USB2.0", - &dibusb_device_classes[DTT200U], - { &dib_table[2], NULL }, - { &dib_table[3], NULL }, - }, - { "Hanftek UMT-010 DVB-T USB2.0", - &dibusb_device_classes[UMT2_0], - { &dib_table[28], NULL }, - { &dib_table[29], NULL }, - }, - { "KWorld/ADSTech Instant DVB-T USB 2.0", - &dibusb_device_classes[DIBUSB2_0B], - { &dib_table[32], NULL }, - { &dib_table[33], NULL }, /* device ID with default DIBUSB2_0-firmware */ - }, -#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES - { "Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)", - &dibusb_device_classes[DIBUSB1_1_AN2235], - { &dib_table[34], NULL }, - { NULL }, - }, - { "Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)", - &dibusb_device_classes[DTT200U], - { &dib_table[35], NULL }, - { &dib_table[36], NULL }, /* undefined, it could be that the device will get another USB ID in warm state */ - }, - { "DiBcom USB1.1 DVB-T reference design (MOD3000) with AN2135 default IDs", - &dibusb_device_classes[DIBUSB1_1], - { &dib_table[37], NULL }, - { NULL }, - }, -#endif -}; - -static int dibusb_exit(struct usb_dibusb *dib) -{ - deb_info("init_state before exiting everything: %x\n",dib->init_state); - dibusb_remote_exit(dib); - dibusb_fe_exit(dib); - dibusb_i2c_exit(dib); - dibusb_dvb_exit(dib); - dibusb_urb_exit(dib); - deb_info("init_state should be zero now: %x\n",dib->init_state); - dib->init_state = DIBUSB_STATE_INIT; - kfree(dib); - return 0; -} - -static int dibusb_init(struct usb_dibusb *dib) -{ - int ret = 0; - sema_init(&dib->usb_sem, 1); - sema_init(&dib->i2c_sem, 1); - - dib->init_state = DIBUSB_STATE_INIT; - - if ((ret = dibusb_urb_init(dib)) || - (ret = dibusb_dvb_init(dib)) || - (ret = dibusb_i2c_init(dib))) { - dibusb_exit(dib); - return ret; - } - - if ((ret = dibusb_fe_init(dib))) - err("could not initialize a frontend."); - - if ((ret = dibusb_remote_init(dib))) - err("could not initialize remote control."); - - return 0; -} - -static struct dibusb_usb_device * dibusb_device_class_quirk(struct usb_device *udev, struct dibusb_usb_device *dev) -{ - int i; - - /* Quirk for the Kworld/ADSTech Instant USB2.0 device. It has the same USB - * IDs like the USB1.1 KWorld after loading the firmware. Which is a bad - * idea and make this quirk necessary. - */ - if (dev->dev_cl->id == DIBUSB1_1 && udev->speed == USB_SPEED_HIGH) { - info("this seems to be the Kworld/ADSTech Instant USB2.0 device or equal."); - for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) { - if (dibusb_devices[i].dev_cl->id == DIBUSB2_0B) { - dev = &dibusb_devices[i]; - break; - } - } - } - - return dev; -} - -static struct dibusb_usb_device * dibusb_find_device (struct usb_device *udev,int *cold) -{ - int i,j; - struct dibusb_usb_device *dev = NULL; - *cold = -1; - - for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) { - for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].cold_ids[j] != NULL; j++) { - deb_info("check for cold %x %x\n",dibusb_devices[i].cold_ids[j]->idVendor, dibusb_devices[i].cold_ids[j]->idProduct); - if (dibusb_devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && - dibusb_devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { - *cold = 1; - dev = &dibusb_devices[i]; - break; - } - } - - if (dev != NULL) - break; - - for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].warm_ids[j] != NULL; j++) { - deb_info("check for warm %x %x\n",dibusb_devices[i].warm_ids[j]->idVendor, dibusb_devices[i].warm_ids[j]->idProduct); - if (dibusb_devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && - dibusb_devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { - *cold = 0; - dev = &dibusb_devices[i]; - break; - } - } - } - - if (dev != NULL) - dev = dibusb_device_class_quirk(udev,dev); - - return dev; -} - -/* - * USB - */ -static int dibusb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_dibusb *dib = NULL; - struct dibusb_usb_device *dibdev = NULL; - - int ret = -ENOMEM,cold=0; - - if ((dibdev = dibusb_find_device(udev,&cold)) == NULL) { - err("something went very wrong, " - "unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct)); - return -ENODEV; - } - - if (cold == 1) { - info("found a '%s' in cold state, will try to load a firmware",dibdev->name); - ret = dibusb_loadfirmware(udev,dibdev); - } else { - info("found a '%s' in warm state.",dibdev->name); - dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL); - if (dib == NULL) { - err("no memory"); - return ret; - } - memset(dib,0,sizeof(struct usb_dibusb)); - - dib->udev = udev; - dib->dibdev = dibdev; - - /* store parameters to structures */ - dib->rc_query_interval = rc_query_interval; - dib->pid_parse = pid_parse; - dib->rc_key_repeat_count = rc_key_repeat_count; - - usb_set_intfdata(intf, dib); - - ret = dibusb_init(dib); - } - - if (ret == 0) - info("%s successfully initialized and connected.",dibdev->name); - else - info("%s error while loading driver (%d)",dibdev->name,ret); - return ret; -} - -static void dibusb_disconnect(struct usb_interface *intf) -{ - struct usb_dibusb *dib = usb_get_intfdata(intf); - const char *name = DRIVER_DESC; - - usb_set_intfdata(intf,NULL); - if (dib != NULL && dib->dibdev != NULL) { - name = dib->dibdev->name; - dibusb_exit(dib); - } - info("%s successfully deinitialized and disconnected.",name); - -} - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver dibusb_driver = { - .owner = THIS_MODULE, - .name = DRIVER_DESC, - .probe = dibusb_probe, - .disconnect = dibusb_disconnect, - .id_table = dib_table, -}; - -/* module stuff */ -static int __init usb_dibusb_init(void) -{ - int result; - if ((result = usb_register(&dibusb_driver))) { - err("usb_register failed. Error number %d",result); - return result; - } - - return 0; -} - -static void __exit usb_dibusb_exit(void) -{ - /* deregister this driver from the USB subsystem */ - usb_deregister(&dibusb_driver); -} - -module_init (usb_dibusb_init); -module_exit (usb_dibusb_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c deleted file mode 100644 index 400b439e804e..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * dvb-dibusb-dvb.c is part of the driver for mobile USB Budget DVB-T devices - * based on reference design made by DiBcom (http://www.dibcom.fr/) - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * see dvb-dibusb-core.c for more copyright details. - * - * This file contains functions for initializing and handling the - * linux-dvb API. - */ -#include "dvb-dibusb.h" - -#include -#include - -static u32 urb_compl_count; - -/* - * MPEG2 TS DVB stuff - */ -void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs) -{ - struct usb_dibusb *dib = urb->context; - - deb_ts("urb complete feedcount: %d, status: %d, length: %d\n",dib->feedcount,urb->status, - urb->actual_length); - - urb_compl_count++; - if (urb_compl_count % 1000 == 0) - deb_info("%d urbs completed so far.\n",urb_compl_count); - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - deb_ts("urb completition error %d.", urb->status); - break; - } - - if (dib->feedcount > 0 && urb->actual_length > 0) { - if (dib->init_state & DIBUSB_STATE_DVB) - dvb_dmx_swfilter(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length); - } else - deb_ts("URB dropped because of feedcount.\n"); - - usb_submit_urb(urb,GFP_ATOMIC); -} - -static int dibusb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) -{ - struct usb_dibusb *dib = dvbdmxfeed->demux->priv; - int newfeedcount; - - if (dib == NULL) - return -ENODEV; - - newfeedcount = dib->feedcount + (onoff ? 1 : -1); - - /* - * stop feed before setting a new pid if there will be no pid anymore - */ - if (newfeedcount == 0) { - deb_ts("stop feeding\n"); - if (dib->xfer_ops.fifo_ctrl != NULL) { - if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) { - err("error while inhibiting fifo."); - return -ENODEV; - } - } - dibusb_streaming(dib,0); - } - - dib->feedcount = newfeedcount; - - /* activate the pid on the device specific pid_filter */ - deb_ts("setting pid: %5d %04x at index %d '%s'\n",dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? "on" : "off"); - if (dib->pid_parse && dib->xfer_ops.pid_ctrl != NULL) - dib->xfer_ops.pid_ctrl(dib->fe,dvbdmxfeed->index,dvbdmxfeed->pid,onoff); - - /* - * start the feed if this was the first pid to set and there is still a pid - * for reception. - */ - if (dib->feedcount == onoff && dib->feedcount > 0) { - - deb_ts("controlling pid parser\n"); - if (dib->xfer_ops.pid_parse != NULL) { - if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) { - err("could not handle pid_parser"); - } - } - - deb_ts("start feeding\n"); - if (dib->xfer_ops.fifo_ctrl != NULL) { - if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) { - err("error while enabling fifo."); - return -ENODEV; - } - } - dibusb_streaming(dib,1); - } - return 0; -} - -static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); - return dibusb_ctrl_feed(dvbdmxfeed,1); -} - -static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type); - return dibusb_ctrl_feed(dvbdmxfeed,0); -} - -int dibusb_dvb_init(struct usb_dibusb *dib) -{ - int ret; - - urb_compl_count = 0; - - if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC, - THIS_MODULE)) < 0) { - deb_info("dvb_register_adapter failed: error %d", ret); - goto err; - } - dib->adapter.priv = dib; - -/* i2c is done in dibusb_i2c_init */ - - dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; - - dib->demux.priv = (void *)dib; - /* get pidcount from demod */ - dib->demux.feednum = dib->demux.filternum = 255; - dib->demux.start_feed = dibusb_start_feed; - dib->demux.stop_feed = dibusb_stop_feed; - dib->demux.write_to_decoder = NULL; - if ((ret = dvb_dmx_init(&dib->demux)) < 0) { - err("dvb_dmx_init failed: error %d",ret); - goto err_dmx; - } - - dib->dmxdev.filternum = dib->demux.filternum; - dib->dmxdev.demux = &dib->demux.dmx; - dib->dmxdev.capabilities = 0; - if ((ret = dvb_dmxdev_init(&dib->dmxdev, &dib->adapter)) < 0) { - err("dvb_dmxdev_init failed: error %d",ret); - goto err_dmx_dev; - } - - dvb_net_init(&dib->adapter, &dib->dvb_net, &dib->demux.dmx); - - goto success; -err_dmx_dev: - dvb_dmx_release(&dib->demux); -err_dmx: - dvb_unregister_adapter(&dib->adapter); -err: - return ret; -success: - dib->init_state |= DIBUSB_STATE_DVB; - return 0; -} - -int dibusb_dvb_exit(struct usb_dibusb *dib) -{ - if (dib->init_state & DIBUSB_STATE_DVB) { - dib->init_state &= ~DIBUSB_STATE_DVB; - deb_info("unregistering DVB part\n"); - dvb_net_release(&dib->dvb_net); - dib->demux.dmx.close(&dib->demux.dmx); - dvb_dmxdev_release(&dib->dmxdev); - dvb_dmx_release(&dib->demux); - dvb_unregister_adapter(&dib->adapter); - } - return 0; -} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c deleted file mode 100644 index 5a71b88797d9..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * dvb-dibusb-fe-i2c.c is part of the driver for mobile USB Budget DVB-T devices - * based on reference design made by DiBcom (http://www.dibcom.fr/) - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * see dvb-dibusb-core.c for more copyright details. - * - * This file contains functions for attaching, initializing of an appropriate - * demodulator/frontend. I2C-stuff is also located here. - * - */ -#include "dvb-dibusb.h" - -#include - -static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, - u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) -{ - u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ - /* write only ? */ - int wo = (rbuf == NULL || rlen == 0), - len = 2 + wlen + (wo ? 0 : 2); - - sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; - sndbuf[1] = (addr << 1) | (wo ? 0 : 1); - - memcpy(&sndbuf[2],wbuf,wlen); - - if (!wo) { - sndbuf[wlen+2] = (rlen >> 8) & 0xff; - sndbuf[wlen+3] = rlen & 0xff; - } - - return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen); -} - -/* - * I2C master xfer function - */ -static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num) -{ - struct usb_dibusb *dib = i2c_get_adapdata(adap); - int i; - - if (down_interruptible(&dib->i2c_sem) < 0) - return -EAGAIN; - - if (num > 2) - warn("more than 2 i2c messages at a time is not handled yet. TODO."); - - for (i = 0; i < num; i++) { - /* write/read request */ - if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { - if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len, - msg[i+1].buf,msg[i+1].len) < 0) - break; - i++; - } else - if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) - break; - } - - up(&dib->i2c_sem); - return i; -} - -static u32 dibusb_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm dibusb_algo = { - .name = "DiBcom USB i2c algorithm", - .id = I2C_ALGO_BIT, - .master_xfer = dibusb_i2c_xfer, - .functionality = dibusb_i2c_func, -}; - -static int dibusb_general_demod_init(struct dvb_frontend *fe); -static u8 dibusb_general_pll_addr(struct dvb_frontend *fe); -static int dibusb_general_pll_init(struct dvb_frontend *fe, u8 pll_buf[5]); -static int dibusb_general_pll_set(struct dvb_frontend *fe, - struct dvb_frontend_parameters* params, u8 pll_buf[5]); - -static struct mt352_config mt352_hanftek_umt_010_config = { - .demod_address = 0x1e, - .demod_init = dibusb_general_demod_init, - .pll_set = dibusb_general_pll_set, -}; - -static int dibusb_tuner_quirk(struct usb_dibusb *dib) -{ - switch (dib->dibdev->dev_cl->id) { - case DIBUSB1_1: /* some these device have the ENV77H11D5 and some the THOMSON CABLE */ - case DIBUSB1_1_AN2235: { /* actually its this device, but in warm state they are indistinguishable */ - struct dibusb_tuner *t; - u8 b[2] = { 0,0 } ,b2[1]; - struct i2c_msg msg[2] = { - { .flags = 0, .buf = b, .len = 2 }, - { .flags = I2C_M_RD, .buf = b2, .len = 1}, - }; - - t = &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5]; - - msg[0].addr = msg[1].addr = t->pll_addr; - - if (dib->xfer_ops.tuner_pass_ctrl != NULL) - dib->xfer_ops.tuner_pass_ctrl(dib->fe,1,t->pll_addr); - dibusb_i2c_xfer(&dib->i2c_adap,msg,2); - if (dib->xfer_ops.tuner_pass_ctrl != NULL) - dib->xfer_ops.tuner_pass_ctrl(dib->fe,0,t->pll_addr); - - if (b2[0] == 0xfe) - info("this device has the Thomson Cable onboard. Which is default."); - else { - dib->tuner = t; - info("this device has the Panasonic ENV77H11D5 onboard."); - } - break; - } - default: - break; - } - return 0; -} - -int dibusb_fe_init(struct usb_dibusb* dib) -{ - struct dib3000_config demod_cfg; - int i; - - if (dib->init_state & DIBUSB_STATE_I2C) { - for (i = 0; i < sizeof(dib->dibdev->dev_cl->demod->i2c_addrs) / sizeof(unsigned char) && - dib->dibdev->dev_cl->demod->i2c_addrs[i] != 0; i++) { - - demod_cfg.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i]; - demod_cfg.pll_addr = dibusb_general_pll_addr; - demod_cfg.pll_set = dibusb_general_pll_set; - demod_cfg.pll_init = dibusb_general_pll_init; - - deb_info("demod id: %d %d\n",dib->dibdev->dev_cl->demod->id,DTT200U_FE); - - switch (dib->dibdev->dev_cl->demod->id) { - case DIBUSB_DIB3000MB: - dib->fe = dib3000mb_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops); - break; - case DIBUSB_DIB3000MC: - dib->fe = dib3000mc_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops); - break; - case DIBUSB_MT352: - mt352_hanftek_umt_010_config.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i]; - dib->fe = mt352_attach(&mt352_hanftek_umt_010_config, &dib->i2c_adap); - break; - case DTT200U_FE: - dib->fe = dtt200u_fe_attach(dib,&dib->xfer_ops); - break; - } - if (dib->fe != NULL) { - info("found demodulator at i2c address 0x%x",dib->dibdev->dev_cl->demod->i2c_addrs[i]); - break; - } - } - /* if a frontend was found */ - if (dib->fe != NULL) { - if (dib->fe->ops->sleep != NULL) - dib->fe_sleep = dib->fe->ops->sleep; - dib->fe->ops->sleep = dibusb_hw_sleep; - - if (dib->fe->ops->init != NULL ) - dib->fe_init = dib->fe->ops->init; - dib->fe->ops->init = dibusb_hw_wakeup; - - /* setting the default tuner */ - dib->tuner = dib->dibdev->dev_cl->tuner; - - /* check which tuner is mounted on this device, in case this is unsure */ - dibusb_tuner_quirk(dib); - } - } - if (dib->fe == NULL) { - err("A frontend driver was not found for device '%s'.", - dib->dibdev->name); - return -ENODEV; - } else { - if (dvb_register_frontend(&dib->adapter, dib->fe)) { - err("Frontend registration failed."); - if (dib->fe->ops->release) - dib->fe->ops->release(dib->fe); - dib->fe = NULL; - return -ENODEV; - } - } - - return 0; -} - -int dibusb_fe_exit(struct usb_dibusb *dib) -{ - if (dib->fe != NULL) - dvb_unregister_frontend(dib->fe); - return 0; -} - -int dibusb_i2c_init(struct usb_dibusb *dib) -{ - int ret = 0; - - dib->adapter.priv = dib; - - strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE); -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, -#else - dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL, -#endif - dib->i2c_adap.algo = &dibusb_algo; - dib->i2c_adap.algo_data = NULL; - dib->i2c_adap.id = I2C_ALGO_BIT; - - i2c_set_adapdata(&dib->i2c_adap, dib); - - if ((ret = i2c_add_adapter(&dib->i2c_adap)) < 0) - err("could not add i2c adapter"); - - dib->init_state |= DIBUSB_STATE_I2C; - - return ret; -} - -int dibusb_i2c_exit(struct usb_dibusb *dib) -{ - if (dib->init_state & DIBUSB_STATE_I2C) - i2c_del_adapter(&dib->i2c_adap); - dib->init_state &= ~DIBUSB_STATE_I2C; - return 0; -} - - -/* pll stuff, maybe removed soon (thx to Gerd/Andrew in advance) */ -static int thomson_cable_eu_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4]) -{ - u32 tfreq = (fep->frequency + 36125000) / 62500; - int vu,p0,p1,p2; - - if (fep->frequency > 403250000) - vu = 1, p2 = 1, p1 = 0, p0 = 1; - else if (fep->frequency > 115750000) - vu = 0, p2 = 1, p1 = 1, p0 = 0; - else if (fep->frequency > 44250000) - vu = 0, p2 = 0, p1 = 1, p0 = 1; - else - return -EINVAL; - - pllbuf[0] = (tfreq >> 8) & 0x7f; - pllbuf[1] = tfreq & 0xff; - pllbuf[2] = 0x8e; - pllbuf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0; - return 0; -} - -static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4]) -{ - u32 freq_khz = fep->frequency / 1000; - u32 tfreq = ((freq_khz + 36125)*6 + 500) / 1000; - u8 TA, T210, R210, ctrl1, cp210, p4321; - if (freq_khz > 858000) { - err("frequency cannot be larger than 858 MHz."); - return -EINVAL; - } - - // contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0 - TA = 1; - T210 = 0; - R210 = 0x2; - ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210; - -// ******** CHARGE PUMP CONFIG vs RF FREQUENCIES ***************** - if (freq_khz < 470000) - cp210 = 2; // VHF Low and High band ch E12 to E4 to E12 - else if (freq_khz < 526000) - cp210 = 4; // UHF band Ch E21 to E27 - else // if (freq < 862000000) - cp210 = 5; // UHF band ch E28 to E69 - -//********************* BW select ******************************* - if (freq_khz < 153000) - p4321 = 1; // BW selected for VHF low - else if (freq_khz < 470000) - p4321 = 2; // BW selected for VHF high E5 to E12 - else // if (freq < 862000000) - p4321 = 4; // BW selection for UHF E21 to E69 - - pllbuf[0] = (tfreq >> 8) & 0xff; - pllbuf[1] = (tfreq >> 0) & 0xff; - pllbuf[2] = 0xff & ctrl1; - pllbuf[3] = (cp210 << 5) | (p4321); - - return 0; -} - -/* - * 7 6 5 4 3 2 1 0 - * Address Byte 1 1 0 0 0 MA1 MA0 R/~W=0 - * - * Program divider byte 1 0 n14 n13 n12 n11 n10 n9 n8 - * Program divider byte 2 n7 n6 n5 n4 n3 n2 n1 n0 - * - * Control byte 1 1 T/A=1 T2 T1 T0 R2 R1 R0 - * 1 T/A=0 0 0 ATC AL2 AL1 AL0 - * - * Control byte 2 CP2 CP1 CP0 BS5 BS4 BS3 BS2 BS1 - * - * MA0/1 = programmable address bits - * R/~W = read/write bit (0 for writing) - * N14-0 = programmable LO frequency - * - * T/A = test AGC bit (0 = next 6 bits AGC setting, - * 1 = next 6 bits test and reference divider ratio settings) - * T2-0 = test bits - * R2-0 = reference divider ratio and programmable frequency step - * ATC = AGC current setting and time constant - * ATC = 0: AGC current = 220nA, AGC time constant = 2s - * ATC = 1: AGC current = 9uA, AGC time constant = 50ms - * AL2-0 = AGC take-over point bits - * CP2-0 = charge pump current - * BS5-1 = PMOS ports control bits; - * BSn = 0 corresponding port is off, high-impedance state (at power-on) - * BSn = 1 corresponding port is on - */ -static int panasonic_cofdm_env77h11d5_tda6650_init(struct dvb_frontend *fe, u8 pllbuf[4]) -{ - pllbuf[0] = 0x0b; - pllbuf[1] = 0xf5; - pllbuf[2] = 0x85; - pllbuf[3] = 0xab; - return 0; -} - -static int panasonic_cofdm_env77h11d5_tda6650_set (struct dvb_frontend_parameters *fep,u8 pllbuf[4]) -{ - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = fep->frequency + 36166000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) - cp = 3; - else if (tuner_frequency < 160000000) - cp = 5; - else if (tuner_frequency < 200000000) - cp = 6; - else if (tuner_frequency < 290000000) - cp = 3; - else if (tuner_frequency < 420000000) - cp = 5; - else if (tuner_frequency < 480000000) - cp = 6; - else if (tuner_frequency < 620000000) - cp = 3; - else if (tuner_frequency < 830000000) - cp = 5; - else if (tuner_frequency < 895000000) - cp = 7; - else - return -EINVAL; - - // determine band - if (fep->frequency < 49000000) - return -EINVAL; - else if (fep->frequency < 161000000) - band = 1; - else if (fep->frequency < 444000000) - band = 2; - else if (fep->frequency < 861000000) - band = 4; - else - return -EINVAL; - - // setup PLL filter - switch (fep->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - case BANDWIDTH_7_MHZ: - filter = 0; - break; - case BANDWIDTH_8_MHZ: - filter = 1; - break; - default: - return -EINVAL; - } - - // calculate divisor - // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = (((fep->frequency / 1000) * 6) + 217496) / 1000; - - // setup tuner buffer - pllbuf[0] = (tuner_frequency >> 8) & 0x7f; - pllbuf[1] = tuner_frequency & 0xff; - pllbuf[2] = 0xca; - pllbuf[3] = (cp << 5) | (filter << 3) | band; - return 0; -} - -/* - * 7 6 5 4 3 2 1 0 - * Address Byte 1 1 0 0 0 MA1 MA0 R/~W=0 - * - * Program divider byte 1 0 n14 n13 n12 n11 n10 n9 n8 - * Program divider byte 2 n7 n6 n5 n4 n3 n2 n1 n0 - * - * Control byte 1 CP T2 T1 T0 RSA RSB OS - * - * Band Switch byte X X X P4 P3 P2 P1 P0 - * - * Auxiliary byte ATC AL2 AL1 AL0 0 0 0 0 - * - * Address: MA1 MA0 Address - * 0 0 c0 - * 0 1 c2 (always valid) - * 1 0 c4 - * 1 1 c6 - */ -static int lg_tdtp_e102p_tua6034(struct dvb_frontend_parameters* fep, u8 pllbuf[4]) -{ - u32 div; - u8 p210, p3; - -#define TUNER_MUL 62500 - - div = (fep->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL; -// div = ((fep->frequency/1000 + 36166) * 6) / 1000; - - if (fep->frequency < 174500000) - p210 = 1; // not supported by the tdtp_e102p - else if (fep->frequency < 230000000) // VHF - p210 = 2; - else - p210 = 4; - - if (fep->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) - p3 = 0; - else - p3 = 1; - - pllbuf[0] = (div >> 8) & 0x7f; - pllbuf[1] = div & 0xff; - pllbuf[2] = 0xce; -// pllbuf[2] = 0xcc; - pllbuf[3] = (p3 << 3) | p210; - - return 0; -} - -static int lg_tdtp_e102p_mt352_demod_init(struct dvb_frontend *fe) -{ - static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d }; - static u8 mt352_reset[] = { 0x50, 0x80 }; - static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 }; - static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; - static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 }; - - static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff }; - static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff }; - static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 }; - static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 }; - static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f }; - - static u8 mt352_acq_ctl[] = { 0x53, 0x50 }; - static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 }; - - mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(fe, mt352_reset, sizeof(mt352_reset)); - mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio)); - - mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); - - mt352_write(fe, mt352_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1)); - mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2)); - mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3)); - mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4)); - mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5)); - - mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl)); - mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1)); - - return 0; -} - -static int dibusb_general_demod_init(struct dvb_frontend *fe) -{ - struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; - switch (dib->dibdev->dev_cl->id) { - case UMT2_0: - return lg_tdtp_e102p_mt352_demod_init(fe); - default: /* other device classes do not have device specific demod inits */ - break; - } - return 0; -} - -static u8 dibusb_general_pll_addr(struct dvb_frontend *fe) -{ - struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; - return dib->tuner->pll_addr; -} - -static int dibusb_pll_i2c_helper(struct usb_dibusb *dib, u8 pll_buf[5], u8 buf[4]) -{ - if (pll_buf == NULL) { - struct i2c_msg msg = { - .addr = dib->tuner->pll_addr, - .flags = 0, - .buf = buf, - .len = sizeof(buf) - }; - if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1) - return -EIO; - msleep(1); - } else { - pll_buf[0] = dib->tuner->pll_addr << 1; - memcpy(&pll_buf[1],buf,4); - } - - return 0; -} - -static int dibusb_general_pll_init(struct dvb_frontend *fe, - u8 pll_buf[5]) -{ - struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; - u8 buf[4]; - int ret=0; - switch (dib->tuner->id) { - case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5: - ret = panasonic_cofdm_env77h11d5_tda6650_init(fe,buf); - break; - default: - break; - } - - if (ret) - return ret; - - return dibusb_pll_i2c_helper(dib,pll_buf,buf); -} - -static int dibusb_general_pll_set(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep, u8 pll_buf[5]) -{ - struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv; - u8 buf[4]; - int ret=0; - - switch (dib->tuner->id) { - case DIBUSB_TUNER_CABLE_THOMSON: - ret = thomson_cable_eu_pll_set(fep, buf); - break; - case DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5: - ret = panasonic_cofdm_env57h1xd5_pll_set(fep, buf); - break; - case DIBUSB_TUNER_CABLE_LG_TDTP_E102P: - ret = lg_tdtp_e102p_tua6034(fep, buf); - break; - case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5: - ret = panasonic_cofdm_env77h11d5_tda6650_set(fep,buf); - break; - default: - warn("no pll programming routine found for tuner %d.\n",dib->tuner->id); - ret = -ENODEV; - break; - } - - if (ret) - return ret; - - return dibusb_pll_i2c_helper(dib,pll_buf,buf); -} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c deleted file mode 100644 index 504ba47afdf3..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * dvb-dibusb-firmware.c is part of the driver for mobile USB Budget DVB-T devices - * based on reference design made by DiBcom (http://www.dibcom.fr/) - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * see dvb-dibusb-core.c for more copyright details. - * - * This file contains functions for downloading the firmware to the device. - */ -#include "dvb-dibusb.h" - -#include -#include - -/* - * load a firmware packet to the device - */ -static int dibusb_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len) -{ - return usb_control_msg(udev, usb_sndctrlpipe(udev,0), - 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); -} - -int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev) -{ - const struct firmware *fw = NULL; - u16 addr; - u8 *b,*p; - int ret = 0,i; - - if ((ret = request_firmware(&fw, dibdev->dev_cl->firmware, &udev->dev)) != 0) { - err("did not find the firmware file. (%s) " - "Please see linux/Documentation/dvb/ for more details on firmware-problems.", - dibdev->dev_cl->firmware); - return ret; - } - - info("downloading firmware from file '%s'.",dibdev->dev_cl->firmware); - - p = kmalloc(fw->size,GFP_KERNEL); - if (p != NULL) { - u8 reset; - /* - * you cannot use the fw->data as buffer for - * usb_control_msg, a new buffer has to be - * created - */ - memcpy(p,fw->data,fw->size); - - /* stop the CPU */ - reset = 1; - if ((ret = dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1)) != 1) - err("could not stop the USB controller CPU."); - for(i = 0; p[i+3] == 0 && i < fw->size; ) { - b = (u8 *) &p[i]; - addr = *((u16 *) &b[1]); - - ret = dibusb_writemem(udev,addr,&b[4],b[0]); - - if (ret != b[0]) { - err("error while transferring firmware " - "(transferred size: %d, block size: %d)", - ret,b[0]); - ret = -EINVAL; - break; - } - i += 5 + b[0]; - } - /* length in ret */ - if (ret > 0) - ret = 0; - /* restart the CPU */ - reset = 0; - if (ret || dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1) != 1) { - err("could not restart the USB controller CPU."); - ret = -EINVAL; - } - - kfree(p); - } else { - ret = -ENOMEM; - } - release_firmware(fw); - - return ret; -} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c deleted file mode 100644 index 9dc8b15517b7..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * dvb-dibusb-remote.c is part of the driver for mobile USB Budget DVB-T devices - * based on reference design made by DiBcom (http://www.dibcom.fr/) - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * see dvb-dibusb-core.c for more copyright details. - * - * This file contains functions for handling the event device on the software - * side and the remote control on the hardware side. - */ -#include "dvb-dibusb.h" - -/* Table to map raw key codes to key events. This should not be hard-wired - into the kernel. */ -static const struct { u8 c0, c1, c2; uint32_t key; } nec_rc_keys [] = -{ - /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ - { 0x00, 0xff, 0x16, KEY_POWER }, - { 0x00, 0xff, 0x10, KEY_MUTE }, - { 0x00, 0xff, 0x03, KEY_1 }, - { 0x00, 0xff, 0x01, KEY_2 }, - { 0x00, 0xff, 0x06, KEY_3 }, - { 0x00, 0xff, 0x09, KEY_4 }, - { 0x00, 0xff, 0x1d, KEY_5 }, - { 0x00, 0xff, 0x1f, KEY_6 }, - { 0x00, 0xff, 0x0d, KEY_7 }, - { 0x00, 0xff, 0x19, KEY_8 }, - { 0x00, 0xff, 0x1b, KEY_9 }, - { 0x00, 0xff, 0x15, KEY_0 }, - { 0x00, 0xff, 0x05, KEY_CHANNELUP }, - { 0x00, 0xff, 0x02, KEY_CHANNELDOWN }, - { 0x00, 0xff, 0x1e, KEY_VOLUMEUP }, - { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN }, - { 0x00, 0xff, 0x11, KEY_RECORD }, - { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ - { 0x00, 0xff, 0x14, KEY_PLAY }, - { 0x00, 0xff, 0x1a, KEY_STOP }, - { 0x00, 0xff, 0x40, KEY_REWIND }, - { 0x00, 0xff, 0x12, KEY_FASTFORWARD }, - { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ - { 0x00, 0xff, 0x4c, KEY_PAUSE }, - { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */ - { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ - /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ - { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */ - { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */ - { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */ - { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */ - { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */ - { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */ - /* Key codes for the KWorld/ADSTech/JetWay remote. */ - { 0x86, 0x6b, 0x12, KEY_POWER }, - { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */ - { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */ - { 0x86, 0x6b, 0x0b, KEY_EPG }, - { 0x86, 0x6b, 0x10, KEY_MUTE }, - { 0x86, 0x6b, 0x01, KEY_1 }, - { 0x86, 0x6b, 0x02, KEY_2 }, - { 0x86, 0x6b, 0x03, KEY_3 }, - { 0x86, 0x6b, 0x04, KEY_4 }, - { 0x86, 0x6b, 0x05, KEY_5 }, - { 0x86, 0x6b, 0x06, KEY_6 }, - { 0x86, 0x6b, 0x07, KEY_7 }, - { 0x86, 0x6b, 0x08, KEY_8 }, - { 0x86, 0x6b, 0x09, KEY_9 }, - { 0x86, 0x6b, 0x0a, KEY_0 }, - { 0x86, 0x6b, 0x18, KEY_ZOOM }, - { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */ - { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */ - { 0x86, 0x6b, 0x00, KEY_UNDO }, - { 0x86, 0x6b, 0x1d, KEY_RECORD }, - { 0x86, 0x6b, 0x0d, KEY_STOP }, - { 0x86, 0x6b, 0x0e, KEY_PAUSE }, - { 0x86, 0x6b, 0x16, KEY_PLAY }, - { 0x86, 0x6b, 0x11, KEY_BACK }, - { 0x86, 0x6b, 0x19, KEY_FORWARD }, - { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */ - { 0x86, 0x6b, 0x15, KEY_ESC }, - { 0x86, 0x6b, 0x1a, KEY_UP }, - { 0x86, 0x6b, 0x1e, KEY_DOWN }, - { 0x86, 0x6b, 0x1f, KEY_LEFT }, - { 0x86, 0x6b, 0x1b, KEY_RIGHT }, -}; - -/* Hauppauge NOVA-T USB2 keys */ -static const struct { u16 raw; uint32_t key; } haupp_rc_keys [] = { - { 0xddf, KEY_GOTO }, - { 0xdef, KEY_POWER }, - { 0xce7, KEY_TV }, - { 0xcc7, KEY_VIDEO }, - { 0xccf, KEY_AUDIO }, - { 0xcd7, KEY_MEDIA }, - { 0xcdf, KEY_EPG }, - { 0xca7, KEY_UP }, - { 0xc67, KEY_RADIO }, - { 0xcb7, KEY_LEFT }, - { 0xd2f, KEY_OK }, - { 0xcbf, KEY_RIGHT }, - { 0xcff, KEY_BACK }, - { 0xcaf, KEY_DOWN }, - { 0xc6f, KEY_MENU }, - { 0xc87, KEY_VOLUMEUP }, - { 0xc8f, KEY_VOLUMEDOWN }, - { 0xc97, KEY_CHANNEL }, - { 0xc7f, KEY_MUTE }, - { 0xd07, KEY_CHANNELUP }, - { 0xd0f, KEY_CHANNELDOWN }, - { 0xdbf, KEY_RECORD }, - { 0xdb7, KEY_STOP }, - { 0xd97, KEY_REWIND }, - { 0xdaf, KEY_PLAY }, - { 0xda7, KEY_FASTFORWARD }, - { 0xd27, KEY_LAST }, /* Skip backwards */ - { 0xd87, KEY_PAUSE }, - { 0xcf7, KEY_NEXT }, - { 0xc07, KEY_0 }, - { 0xc0f, KEY_1 }, - { 0xc17, KEY_2 }, - { 0xc1f, KEY_3 }, - { 0xc27, KEY_4 }, - { 0xc2f, KEY_5 }, - { 0xc37, KEY_6 }, - { 0xc3f, KEY_7 }, - { 0xc47, KEY_8 }, - { 0xc4f, KEY_9 }, - { 0xc57, KEY_KPASTERISK }, - { 0xc77, KEY_GRAVE }, /* # */ - { 0xc5f, KEY_RED }, - { 0xd77, KEY_GREEN }, - { 0xdc7, KEY_YELLOW }, - { 0xd4f, KEY_BLUE}, -}; - -static int dibusb_key2event_nec(struct usb_dibusb *dib,u8 rb[5]) -{ - int i; - switch (rb[0]) { - case DIBUSB_RC_NEC_KEY_PRESSED: - /* rb[1-3] is the actual key, rb[4] is a checksum */ - deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", - rb[1], rb[2], rb[3], rb[4]); - - if ((0xff - rb[3]) != rb[4]) { - deb_rc("remote control checksum failed.\n"); - break; - } - - /* See if we can match the raw key code. */ - for (i = 0; i < sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) { - if (nec_rc_keys[i].c0 == rb[1] && - nec_rc_keys[i].c1 == rb[2] && - nec_rc_keys[i].c2 == rb[3]) { - - dib->last_event = nec_rc_keys[i].key; - return 1; - } - } - break; - case DIBUSB_RC_NEC_KEY_REPEATED: - /* rb[1]..rb[4] are always zero.*/ - /* Repeats often seem to occur so for the moment just ignore this. */ - return 0; - case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */ - default: - break; - } - return -1; -} - -static int dibusb_key2event_hauppauge(struct usb_dibusb *dib,u8 rb[4]) -{ - u16 raw; - int i,state; - switch (rb[0]) { - case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: - raw = ((rb[1] & 0x0f) << 8) | rb[2]; - - state = !!(rb[1] & 0x40); - - deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to %04x state: %d\n",rb[1],rb[2],rb[3],raw,state); - for (i = 0; i < sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) { - if (haupp_rc_keys[i].raw == raw) { - if (dib->last_event == haupp_rc_keys[i].key && - dib->last_state == state) { - deb_rc("key repeat\n"); - return 0; - } else { - dib->last_event = haupp_rc_keys[i].key; - dib->last_state = state; - return 1; - } - } - } - - break; - case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: - default: - break; - } - return -1; -} - -/* - * Read the remote control and feed the appropriate event. - * NEC protocol is used for remote controls - */ -static int dibusb_read_remote_control(struct usb_dibusb *dib) -{ - u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5]; - int ret,event = 0; - - if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5))) - return ret; - - switch (dib->dibdev->dev_cl->remote_type) { - case DIBUSB_RC_NEC_PROTOCOL: - event = dibusb_key2event_nec(dib,rb); - break; - case DIBUSB_RC_HAUPPAUGE_PROTO: - event = dibusb_key2event_hauppauge(dib,rb); - default: - break; - } - - /* key repeat */ - if (event == 0) - if (++dib->repeat_key_count < dib->rc_key_repeat_count) { - deb_rc("key repeat dropped. (%d)\n",dib->repeat_key_count); - event = -1; /* skip this key repeat */ - } - - if (event == 1 || event == 0) { - deb_rc("Translated key 0x%04x\n",event); - - /* Signal down and up events for this key. */ - input_report_key(&dib->rc_input_dev, dib->last_event, 1); - input_report_key(&dib->rc_input_dev, dib->last_event, 0); - input_sync(&dib->rc_input_dev); - - if (event == 1) - dib->repeat_key_count = 0; - } - return 0; -} - -/* Remote-control poll function - called every dib->rc_query_interval ms to see - whether the remote control has received anything. */ -static void dibusb_remote_query(void *data) -{ - struct usb_dibusb *dib = (struct usb_dibusb *) data; - /* TODO: need a lock here. We can simply skip checking for the remote control - if we're busy. */ - dibusb_read_remote_control(dib); - schedule_delayed_work(&dib->rc_query_work, - msecs_to_jiffies(dib->rc_query_interval)); -} - -int dibusb_remote_init(struct usb_dibusb *dib) -{ - int i; - - if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO) - return 0; - - /* Initialise the remote-control structures.*/ - init_input_dev(&dib->rc_input_dev); - - dib->rc_input_dev.evbit[0] = BIT(EV_KEY); - dib->rc_input_dev.keycodesize = sizeof(unsigned char); - dib->rc_input_dev.keycodemax = KEY_MAX; - dib->rc_input_dev.name = DRIVER_DESC " remote control"; - - switch (dib->dibdev->dev_cl->remote_type) { - case DIBUSB_RC_NEC_PROTOCOL: - for (i=0; irc_input_dev.keybit); - break; - case DIBUSB_RC_HAUPPAUGE_PROTO: - for (i=0; irc_input_dev.keybit); - break; - default: - break; - } - - - input_register_device(&dib->rc_input_dev); - - INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib); - - /* Start the remote-control polling. */ - if (dib->rc_query_interval < 40) - dib->rc_query_interval = 100; /* default */ - - info("schedule remote query interval to %d msecs.",dib->rc_query_interval); - schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval)); - - dib->init_state |= DIBUSB_STATE_REMOTE; - - return 0; -} - -int dibusb_remote_exit(struct usb_dibusb *dib) -{ - if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO) - return 0; - - if (dib->init_state & DIBUSB_STATE_REMOTE) { - cancel_delayed_work(&dib->rc_query_work); - flush_scheduled_work(); - input_unregister_device(&dib->rc_input_dev); - } - dib->init_state &= ~DIBUSB_STATE_REMOTE; - return 0; -} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-usb.c b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c deleted file mode 100644 index 642f0596a5ba..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb-usb.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * dvb-dibusb-usb.c is part of the driver for mobile USB Budget DVB-T devices - * based on reference design made by DiBcom (http://www.dibcom.fr/) - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * see dvb-dibusb-core.c for more copyright details. - * - * This file contains functions for initializing and handling the - * usb specific stuff. - */ -#include "dvb-dibusb.h" - -#include -#include - -int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf, - u16 rlen) -{ - int actlen,ret = -ENOMEM; - - if (wbuf == NULL || wlen == 0) - return -EINVAL; - - if ((ret = down_interruptible(&dib->usb_sem))) - return ret; - - debug_dump(wbuf,wlen); - - ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev, - dib->dibdev->dev_cl->pipe_cmd), wbuf,wlen,&actlen, - DIBUSB_I2C_TIMEOUT); - - if (ret) - err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); - else - ret = actlen != wlen ? -1 : 0; - - /* an answer is expected, and no error before */ - if (!ret && rbuf && rlen) { - ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev, - dib->dibdev->dev_cl->pipe_cmd),rbuf,rlen,&actlen, - DIBUSB_I2C_TIMEOUT); - - if (ret) - err("recv bulk message failed: %d",ret); - else { - deb_alot("rlen: %d\n",rlen); - debug_dump(rbuf,actlen); - } - } - - up(&dib->usb_sem); - return ret; -} - -/* - * Cypress controls - */ -int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len) -{ - return dibusb_readwrite_usb(dib,buf,len,NULL,0); -} - -#if 0 -/* - * #if 0'ing the following functions as they are not in use _now_, - * but probably will be sometime. - */ -/* - * do not use this, just a workaround for a bug, - * which will hopefully never occur :). - */ -int dibusb_interrupt_read_loop(struct usb_dibusb *dib) -{ - u8 b[1] = { DIBUSB_REQ_INTR_READ }; - return dibusb_write_usb(dib,b,1); -} -#endif - -/* - * ioctl for the firmware - */ -static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen) -{ - u8 b[34]; - int size = plen > 32 ? 32 : plen; - memset(b,0,34); - b[0] = DIBUSB_REQ_SET_IOCTL; - b[1] = cmd; - - if (size > 0) - memcpy(&b[2],param,size); - - return dibusb_write_usb(dib,b,34); //2+size); -} - -/* - * ioctl for power control - */ -int dibusb_hw_wakeup(struct dvb_frontend *fe) -{ - struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv; - u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP }; - deb_info("dibusb-device is getting up.\n"); - - switch (dib->dibdev->dev_cl->id) { - case DTT200U: - break; - default: - dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); - break; - } - - if (dib->fe_init) - return dib->fe_init(fe); - - return 0; -} - -int dibusb_hw_sleep(struct dvb_frontend *fe) -{ - struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv; - u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP }; - deb_info("dibusb-device is going to bed.\n"); - /* workaround, something is wrong, when dibusb 1.1 device are going to bed too late */ - switch (dib->dibdev->dev_cl->id) { - case DIBUSB1_1: - case NOVAT_USB2: - case DTT200U: - break; - default: - dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); - break; - } - if (dib->fe_sleep) - return dib->fe_sleep(fe); - - return 0; -} - -int dibusb_set_streaming_mode(struct usb_dibusb *dib,u8 mode) -{ - u8 b[2] = { DIBUSB_REQ_SET_STREAMING_MODE, mode }; - return dibusb_readwrite_usb(dib,b,2,NULL,0); -} - -static int dibusb_urb_kill(struct usb_dibusb *dib) -{ - int i; -deb_info("trying to kill urbs\n"); - if (dib->init_state & DIBUSB_STATE_URB_SUBMIT) { - for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { - deb_info("killing URB no. %d.\n",i); - - /* stop the URB */ - usb_kill_urb(dib->urb_list[i]); - } - } else - deb_info(" URBs not killed.\n"); - dib->init_state &= ~DIBUSB_STATE_URB_SUBMIT; - return 0; -} - -static int dibusb_urb_submit(struct usb_dibusb *dib) -{ - int i,ret; - if (dib->init_state & DIBUSB_STATE_URB_INIT) { - for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { - deb_info("submitting URB no. %d\n",i); - if ((ret = usb_submit_urb(dib->urb_list[i],GFP_ATOMIC))) { - err("could not submit buffer urb no. %d - get them all back\n",i); - dibusb_urb_kill(dib); - return ret; - } - dib->init_state |= DIBUSB_STATE_URB_SUBMIT; - } - } - return 0; -} - -int dibusb_streaming(struct usb_dibusb *dib,int onoff) -{ - if (onoff) - dibusb_urb_submit(dib); - else - dibusb_urb_kill(dib); - - switch (dib->dibdev->dev_cl->id) { - case DIBUSB2_0: - case DIBUSB2_0B: - case NOVAT_USB2: - case UMT2_0: - if (onoff) - return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_ENABLE_STREAM,NULL,0); - else - return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_DISABLE_STREAM,NULL,0); - break; - default: - break; - } - return 0; -} - -int dibusb_urb_init(struct usb_dibusb *dib) -{ - int i,bufsize,def_pid_parse = 1; - - /* - * when reloading the driver w/o replugging the device - * a timeout occures, this helps - */ - usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); - usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); - usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data)); - - /* allocate the array for the data transfer URBs */ - dib->urb_list = kmalloc(dib->dibdev->dev_cl->urb_count*sizeof(struct urb *),GFP_KERNEL); - if (dib->urb_list == NULL) - return -ENOMEM; - memset(dib->urb_list,0,dib->dibdev->dev_cl->urb_count*sizeof(struct urb *)); - - dib->init_state |= DIBUSB_STATE_URB_LIST; - - bufsize = dib->dibdev->dev_cl->urb_count*dib->dibdev->dev_cl->urb_buffer_size; - deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize); - /* allocate the actual buffer for the URBs */ - if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) { - deb_info("not enough memory.\n"); - return -ENOMEM; - } - deb_info("allocation complete\n"); - memset(dib->buffer,0,bufsize); - - dib->init_state |= DIBUSB_STATE_URB_BUF; - - /* allocate and submit the URBs */ - for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { - if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) { - return -ENOMEM; - } - - usb_fill_bulk_urb( dib->urb_list[i], dib->udev, - usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data), - &dib->buffer[i*dib->dibdev->dev_cl->urb_buffer_size], - dib->dibdev->dev_cl->urb_buffer_size, - dibusb_urb_complete, dib); - - dib->urb_list[i]->transfer_flags = 0; - - dib->init_state |= DIBUSB_STATE_URB_INIT; - } - - /* dib->pid_parse here contains the value of the module parameter */ - /* decide if pid parsing can be deactivated: - * is possible (by device type) and wanted (by user) - */ - switch (dib->dibdev->dev_cl->id) { - case DIBUSB2_0: - case DIBUSB2_0B: - if (dib->udev->speed == USB_SPEED_HIGH && !dib->pid_parse) { - def_pid_parse = 0; - info("running at HIGH speed, will deliver the complete TS."); - } else - info("will use pid_parsing."); - break; - default: - break; - } - /* from here on it contains the device and user decision */ - dib->pid_parse = def_pid_parse; - - return 0; -} - -int dibusb_urb_exit(struct usb_dibusb *dib) -{ - int i; - - dibusb_urb_kill(dib); - - if (dib->init_state & DIBUSB_STATE_URB_LIST) { - for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { - if (dib->urb_list[i] != NULL) { - deb_info("freeing URB no. %d.\n",i); - /* free the URBs */ - usb_free_urb(dib->urb_list[i]); - } - } - /* free the urb array */ - kfree(dib->urb_list); - dib->init_state &= ~DIBUSB_STATE_URB_LIST; - } - - if (dib->init_state & DIBUSB_STATE_URB_BUF) - pci_free_consistent(NULL, - dib->dibdev->dev_cl->urb_buffer_size*dib->dibdev->dev_cl->urb_count, - dib->buffer,dib->dma_handle); - - dib->init_state &= ~DIBUSB_STATE_URB_BUF; - dib->init_state &= ~DIBUSB_STATE_URB_INIT; - return 0; -} diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.h b/drivers/media/dvb/dibusb/dvb-dibusb.h deleted file mode 100644 index c965b64fb1ab..000000000000 --- a/drivers/media/dvb/dibusb/dvb-dibusb.h +++ /dev/null @@ -1,327 +0,0 @@ -/* - * dvb-dibusb.h - * - * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) - * - * 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, version 2. - * - * for more information see dvb-dibusb-core.c . - */ -#ifndef __DVB_DIBUSB_H__ -#define __DVB_DIBUSB_H__ - -#include -#include -#include - -#include "dvb_frontend.h" -#include "dvb_demux.h" -#include "dvb_net.h" -#include "dmxdev.h" - -#include "dib3000.h" -#include "mt352.h" - -/* debug */ -#ifdef CONFIG_DVB_DIBCOM_DEBUG -#define dprintk(level,args...) \ - do { if ((dvb_dibusb_debug & level)) { printk(args); } } while (0) - -#define debug_dump(b,l) {\ - int i; \ - for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \ - deb_xfer("\n");\ -} - -#else -#define dprintk(args...) -#define debug_dump(b,l) -#endif - -extern int dvb_dibusb_debug; - -/* Version information */ -#define DRIVER_VERSION "0.3" -#define DRIVER_DESC "DiBcom based USB Budget DVB-T device" -#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" - -#define deb_info(args...) dprintk(0x01,args) -#define deb_xfer(args...) dprintk(0x02,args) -#define deb_alot(args...) dprintk(0x04,args) -#define deb_ts(args...) dprintk(0x08,args) -#define deb_err(args...) dprintk(0x10,args) -#define deb_rc(args...) dprintk(0x20,args) - -/* generic log methods - taken from usb.h */ -#undef err -#define err(format, arg...) printk(KERN_ERR "dvb-dibusb: " format "\n" , ## arg) -#undef info -#define info(format, arg...) printk(KERN_INFO "dvb-dibusb: " format "\n" , ## arg) -#undef warn -#define warn(format, arg...) printk(KERN_WARNING "dvb-dibusb: " format "\n" , ## arg) - -struct dibusb_usb_controller { - const char *name; /* name of the usb controller */ - u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */ -}; - -typedef enum { - DIBUSB1_1 = 0, - DIBUSB1_1_AN2235, - DIBUSB2_0, - UMT2_0, - DIBUSB2_0B, - NOVAT_USB2, - DTT200U, -} dibusb_class_t; - -typedef enum { - DIBUSB_TUNER_CABLE_THOMSON = 0, - DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5, - DIBUSB_TUNER_CABLE_LG_TDTP_E102P, - DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5, -} dibusb_tuner_t; - -typedef enum { - DIBUSB_DIB3000MB = 0, - DIBUSB_DIB3000MC, - DIBUSB_MT352, - DTT200U_FE, -} dibusb_demodulator_t; - -typedef enum { - DIBUSB_RC_NO = 0, - DIBUSB_RC_NEC_PROTOCOL, - DIBUSB_RC_HAUPPAUGE_PROTO, -} dibusb_remote_t; - -struct dibusb_tuner { - dibusb_tuner_t id; - - u8 pll_addr; /* tuner i2c address */ -}; -extern struct dibusb_tuner dibusb_tuner[]; - -#define DIBUSB_POSSIBLE_I2C_ADDR_NUM 4 -struct dibusb_demod { - dibusb_demodulator_t id; - - int pid_filter_count; /* counter of the internal pid_filter */ - u8 i2c_addrs[DIBUSB_POSSIBLE_I2C_ADDR_NUM]; /* list of possible i2c addresses of the demod */ -}; - -#define DIBUSB_MAX_TUNER_NUM 2 -struct dibusb_device_class { - dibusb_class_t id; - - const struct dibusb_usb_controller *usb_ctrl; /* usb controller */ - const char *firmware; /* valid firmware filenames */ - - int pipe_cmd; /* command pipe (read/write) */ - int pipe_data; /* data pipe */ - - int urb_count; /* number of data URBs to be submitted */ - int urb_buffer_size; /* the size of the buffer for each URB */ - - dibusb_remote_t remote_type; /* does this device have a ir-receiver */ - - struct dibusb_demod *demod; /* which demodulator is mount */ - struct dibusb_tuner *tuner; /* which tuner can be found here */ -}; - -#define DIBUSB_ID_MAX_NUM 15 -struct dibusb_usb_device { - const char *name; /* real name of the box */ - struct dibusb_device_class *dev_cl; /* which dibusb_device_class is this device part of */ - - struct usb_device_id *cold_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at pre firmware state */ - struct usb_device_id *warm_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at post firmware state */ -}; - -/* a PID for the pid_filter list, when in use */ -struct dibusb_pid -{ - int index; - u16 pid; - int active; -}; - -struct usb_dibusb { - /* usb */ - struct usb_device * udev; - - struct dibusb_usb_device * dibdev; - -#define DIBUSB_STATE_INIT 0x000 -#define DIBUSB_STATE_URB_LIST 0x001 -#define DIBUSB_STATE_URB_BUF 0x002 -#define DIBUSB_STATE_URB_INIT 0x004 -#define DIBUSB_STATE_DVB 0x008 -#define DIBUSB_STATE_I2C 0x010 -#define DIBUSB_STATE_REMOTE 0x020 -#define DIBUSB_STATE_URB_SUBMIT 0x040 - int init_state; - - int feedcount; - struct dib_fe_xfer_ops xfer_ops; - - struct dibusb_tuner *tuner; - - struct urb **urb_list; - u8 *buffer; - dma_addr_t dma_handle; - - /* I2C */ - struct i2c_adapter i2c_adap; - - /* locking */ - struct semaphore usb_sem; - struct semaphore i2c_sem; - - /* dvb */ - struct dvb_adapter adapter; - struct dmxdev dmxdev; - struct dvb_demux demux; - struct dvb_net dvb_net; - struct dvb_frontend* fe; - - int (*fe_sleep) (struct dvb_frontend *); - int (*fe_init) (struct dvb_frontend *); - - /* remote control */ - struct input_dev rc_input_dev; - struct work_struct rc_query_work; - int last_event; - int last_state; /* for Hauppauge RC protocol */ - int repeat_key_count; - int rc_key_repeat_count; /* module parameter */ - - /* module parameters */ - int pid_parse; - int rc_query_interval; -}; - -/* commonly used functions in the separated files */ - -/* dvb-dibusb-firmware.c */ -int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev); - -/* dvb-dibusb-remote.c */ -int dibusb_remote_exit(struct usb_dibusb *dib); -int dibusb_remote_init(struct usb_dibusb *dib); - -/* dvb-dibusb-fe-i2c.c */ -int dibusb_fe_init(struct usb_dibusb* dib); -int dibusb_fe_exit(struct usb_dibusb *dib); -int dibusb_i2c_init(struct usb_dibusb *dib); -int dibusb_i2c_exit(struct usb_dibusb *dib); - -/* dvb-dibusb-dvb.c */ -void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs); -int dibusb_dvb_init(struct usb_dibusb *dib); -int dibusb_dvb_exit(struct usb_dibusb *dib); - -/* dvb-dibusb-usb.c */ -int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf, - u16 rlen); -int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len); - -int dibusb_hw_wakeup(struct dvb_frontend *); -int dibusb_hw_sleep(struct dvb_frontend *); -int dibusb_set_streaming_mode(struct usb_dibusb *,u8); -int dibusb_streaming(struct usb_dibusb *,int); - -int dibusb_urb_init(struct usb_dibusb *); -int dibusb_urb_exit(struct usb_dibusb *); - -/* dvb-fe-dtt200u.c */ -struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *,struct dib_fe_xfer_ops *); - -/* i2c and transfer stuff */ -#define DIBUSB_I2C_TIMEOUT 5000 - -/* - * protocol of all dibusb related devices - */ - -/* - * bulk msg to/from endpoint 0x01 - * - * general structure: - * request_byte parameter_bytes - */ - -#define DIBUSB_REQ_START_READ 0x00 -#define DIBUSB_REQ_START_DEMOD 0x01 - -/* - * i2c read - * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word - * bulk read: byte_buffer (length_word bytes) - */ -#define DIBUSB_REQ_I2C_READ 0x02 - -/* - * i2c write - * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes - */ -#define DIBUSB_REQ_I2C_WRITE 0x03 - -/* - * polling the value of the remote control - * bulk write: 0x04 - * bulk read: byte_buffer (5 bytes) - * - * first byte of byte_buffer shows the status (0x00, 0x01, 0x02) - */ -#define DIBUSB_REQ_POLL_REMOTE 0x04 - -#define DIBUSB_RC_NEC_EMPTY 0x00 -#define DIBUSB_RC_NEC_KEY_PRESSED 0x01 -#define DIBUSB_RC_NEC_KEY_REPEATED 0x02 - -/* additional status values for Hauppauge Remote Control Protocol */ -#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01 -#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03 - -/* streaming mode: - * bulk write: 0x05 mode_byte - * - * mode_byte is mostly 0x00 - */ -#define DIBUSB_REQ_SET_STREAMING_MODE 0x05 - -/* interrupt the internal read loop, when blocking */ -#define DIBUSB_REQ_INTR_READ 0x06 - -/* io control - * 0x07 cmd_byte param_bytes - * - * param_bytes can be up to 32 bytes - * - * cmd_byte function parameter name - * 0x00 power mode - * 0x00 sleep - * 0x01 wakeup - * - * 0x01 enable streaming - * 0x02 disable streaming - * - * - */ -#define DIBUSB_REQ_SET_IOCTL 0x07 - -/* IOCTL commands */ - -/* change the power mode in firmware */ -#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00 -#define DIBUSB_IOCTL_POWER_SLEEP 0x00 -#define DIBUSB_IOCTL_POWER_WAKEUP 0x01 - -/* modify streaming of the FX2 */ -#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01 -#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02 - -#endif diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index d19301d90a09..d6b7a9de471e 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -327,7 +328,8 @@ static int dvb_frontend_is_exiting(struct dvb_frontend *fe) return 1; if (fepriv->dvbdev->writers == 1) - if (jiffies - fepriv->release_jiffies > dvb_shutdown_timeout * HZ) + if (time_after(jiffies, fepriv->release_jiffies + + dvb_shutdown_timeout * HZ)) return 1; return 0; diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig new file mode 100644 index 000000000000..8aa32f6e447b --- /dev/null +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -0,0 +1,99 @@ +config DVB_USB + tristate "Support for various USB DVB devices" + depends on DVB_CORE && USB + select FW_LOADER + help + By enabling this you will be able to choose the various USB 1.1 and + USB2.0 DVB devices. + + Almost every USB device needs a firmware, please look into + + + Say Y if you own an USB DVB device. + +config DVB_USB_DEBUG + bool "Enable extended debug support for all DVB-USB devices" + depends on DVB_USB + help + Say Y if you want to enable debuging. See modinfo dvb-usb (and the + appropriate drivers) for debug levels. + +config DVB_USB_A800 + tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" + depends on DVB_USB + help + Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. + +config DVB_USB_DIBUSB_MB + tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" + depends on DVB_USB + help + Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by + DiBcom () equipped with a DiB3000M-B demodulator. + + Devices supported by this driver: + TwinhanDTV USB-Ter (VP7041) + TwinhanDTV Magic Box (VP7041e) + KWorld/JetWay/ADSTech V-Stream XPERT DTV - DVB-T USB1.1 and USB2.0 + Hama DVB-T USB1.1-Box + DiBcom USB1.1 reference devices (non-public) + Ultima Electronic/Artec T1 USB TVBOX + Compro Videomate DVB-U2000 - DVB-T USB + Grandtec DVB-T USB + Avermedia AverTV DVBT USB1.1 + Artec T1 USB1.1 boxes + + The VP7041 seems to be identical to "CTS Portable" (Chinese + Television System). + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_DIBUSB_MC + tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" + depends on DVB_USB + help + Support for 2.0 DVB-T receivers based on reference designs made by + DiBcom () equipped with a DiB3000M-C/P demodulator. + + Devices supported by this driver: + DiBcom USB2.0 reference devices (non-public) + Artec T1 USB2.0 boxes + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_UMT_010 + tristate "HanfTek UMT-010 DVB-T USB2.0 support" + depends on DVB_USB + help + Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. + +config DVB_USB_DIGITV + tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" + depends on DVB_USB + help + Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. + +config DVB_USB_VP7045 + tristate "TwinhanDTV Alpha/MagicBoxII and DNTV tinyUSB2 DVB-T USB2.0 support" + depends on DVB_USB + help + Say Y here to support the + TwinhanDTV Alpha (stick) (VP-7045), + TwinhanDTV MagicBox II (VP-7046) and + DigitalNow TinyUSB 2 DVB-t DVB-T USB2.0 receivers. + +config DVB_USB_NOVA_T_USB2 + tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" + depends on DVB_USB + help + Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. + +config DVB_USB_DTT200U + tristate "Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 support" + depends on DVB_USB + help + Say Y here to support the Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 receiver. + + The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan). diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile new file mode 100644 index 000000000000..d65b50f9abb0 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -0,0 +1,30 @@ +dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o +obj-$(CONFIG_DVB_USB) += dvb-usb.o + +dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o +obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o + +dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o +obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o + +dvb-usb-dibusb-common-objs = dibusb-common.o + +dvb-usb-a800-objs = a800.o +obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o + +dvb-usb-dibusb-mb-objs = dibusb-mb.o +obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o + +dvb-usb-dibusb-mc-objs = dibusb-mc.o +obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o + +dvb-usb-nova-t-usb2-objs = nova-t-usb2.o +obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o + +dvb-usb-umt-010-objs = umt-010.o +obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o + +dvb-usb-digitv-objs = digitv.o +obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o + +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c new file mode 100644 index 000000000000..a3542935604f --- /dev/null +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -0,0 +1,176 @@ +/* DVB USB framework compliant Linux driver for the AVerMedia AverTV DVB-T + * USB2.0 (A800) DVB-T receiver. + * + * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to + * - AVerMedia who kindly provided information and + * - Glen Harris who suffered from my mistakes during development. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (rc=1 (or-able))." DVB_USB_DEBUG_STATUS); +#define deb_rc(args...) dprintk(debug,0x01,args) + +static int a800_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + /* do nothing for the AVerMedia */ + return 0; +} + +static struct dvb_usb_rc_key a800_rc_keys[] = { + { 0x02, 0x01, KEY_PROG1 }, /* SOURCE */ + { 0x02, 0x00, KEY_POWER }, /* POWER */ + { 0x02, 0x05, KEY_1 }, /* 1 */ + { 0x02, 0x06, KEY_2 }, /* 2 */ + { 0x02, 0x07, KEY_3 }, /* 3 */ + { 0x02, 0x09, KEY_4 }, /* 4 */ + { 0x02, 0x0a, KEY_5 }, /* 5 */ + { 0x02, 0x0b, KEY_6 }, /* 6 */ + { 0x02, 0x0d, KEY_7 }, /* 7 */ + { 0x02, 0x0e, KEY_8 }, /* 8 */ + { 0x02, 0x0f, KEY_9 }, /* 9 */ + { 0x02, 0x12, KEY_LEFT }, /* L / DISPLAY */ + { 0x02, 0x11, KEY_0 }, /* 0 */ + { 0x02, 0x13, KEY_RIGHT }, /* R / CH RTN */ + { 0x02, 0x17, KEY_PROG2 }, /* SNAP SHOT */ + { 0x02, 0x10, KEY_PROG3 }, /* 16-CH PREV */ + { 0x02, 0x03, KEY_CHANNELUP }, /* CH UP */ + { 0x02, 0x1e, KEY_VOLUMEDOWN }, /* VOL DOWN */ + { 0x02, 0x0c, KEY_ZOOM }, /* FULL SCREEN */ + { 0x02, 0x1f, KEY_VOLUMEUP }, /* VOL UP */ + { 0x02, 0x02, KEY_CHANNELDOWN }, /* CH DOWN */ + { 0x02, 0x14, KEY_MUTE }, /* MUTE */ + { 0x02, 0x08, KEY_AUDIO }, /* AUDIO */ + { 0x02, 0x19, KEY_RECORD }, /* RECORD */ + { 0x02, 0x18, KEY_PLAY }, /* PLAY */ + { 0x02, 0x1b, KEY_STOP }, /* STOP */ + { 0x02, 0x1a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ + { 0x02, 0x1d, KEY_BACK }, /* << / RED */ + { 0x02, 0x1c, KEY_FORWARD }, /* >> / YELLOW */ + { 0x02, 0x03, KEY_TEXT }, /* TELETEXT */ + { 0x02, 0x01, KEY_FIRST }, /* |<< / GREEN */ + { 0x02, 0x00, KEY_LAST }, /* >>| / BLUE */ + { 0x02, 0x04, KEY_EPG }, /* EPG */ + { 0x02, 0x15, KEY_MENU }, /* MENU */ +}; + +int a800_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5]; + if (usb_control_msg(d->udev,usb_rcvctrlpipe(d->udev,0), + 0x04, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, key, 5, + 2*HZ) != 5) + return -ENODEV; + + /* call the universal NEC remote processor, to find out the key's state and event */ + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} + +/* USB Driver stuff */ +static struct dvb_usb_properties a800_properties; + +static int a800_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&a800_properties,THIS_MODULE); +} + +/* do not change the order of the ID table */ +static struct usb_device_id a800_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, a800_table); + +static struct dvb_usb_properties a800_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_IS_AN_I2C_ADAPTER, + .pid_filter_count = 32, + + .usb_ctrl = CYPRESS_FX2, + + .firmware = "dvb-usb-avertv-a800-02.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .power_ctrl = a800_power_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = a800_rc_keys, + .rc_key_map_size = ARRAY_SIZE(a800_rc_keys), + .rc_query = a800_rc_query, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { "AVerMedia AverTV DVB-T USB 2.0 (A800)", + { &a800_table[0], NULL }, + { &a800_table[1], NULL }, + }, + } +}; + +static struct usb_driver a800_driver = { + .owner = THIS_MODULE, + .name = "AVerMedia AverTV DVB-T USB 2.0 (A800)", + .probe = a800_probe, + .disconnect = dvb_usb_device_exit, + .id_table = a800_table, +}; + +/* module stuff */ +static int __init a800_module_init(void) +{ + int result; + if ((result = usb_register(&a800_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit a800_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&a800_driver); +} + +module_init (a800_module_init); +module_exit (a800_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("AVerMedia AverTV DVB-T USB 2.0 (A800)"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c new file mode 100644 index 000000000000..63b626f70c81 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -0,0 +1,272 @@ +/* Common methods for dibusb-based-receivers. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS); +MODULE_LICENSE("GPL"); + +#define deb_info(args...) dprintk(debug,0x01,args) + +/* common stuff used by the different dibusb modules */ +int dibusb_streaming_ctrl(struct dvb_usb_device *d, int onoff) +{ + if (d->priv != NULL) { + struct dib_fe_xfer_ops *ops = d->priv; + if (ops->fifo_ctrl != NULL) + if (ops->fifo_ctrl(d->fe,onoff)) { + err("error while controlling the fifo of the demod."); + return -ENODEV; + } + } + return 0; +} +EXPORT_SYMBOL(dibusb_streaming_ctrl); + +int dibusb_pid_filter(struct dvb_usb_device *d, int index, u16 pid, int onoff) +{ + if (d->priv != NULL) { + struct dib_fe_xfer_ops *ops = d->priv; + if (d->pid_filtering && ops->pid_ctrl != NULL) + ops->pid_ctrl(d->fe,index,pid,onoff); + } + return 0; +} +EXPORT_SYMBOL(dibusb_pid_filter); + +int dibusb_pid_filter_ctrl(struct dvb_usb_device *d, int onoff) +{ + if (d->priv != NULL) { + struct dib_fe_xfer_ops *ops = d->priv; + if (ops->pid_parse != NULL) + if (ops->pid_parse(d->fe,onoff) < 0) + err("could not handle pid_parser"); + } + return 0; +} +EXPORT_SYMBOL(dibusb_pid_filter_ctrl); + +int dibusb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b[3]; + int ret; + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = DIBUSB_IOCTL_CMD_POWER_MODE; + b[2] = onoff ? DIBUSB_IOCTL_POWER_WAKEUP : DIBUSB_IOCTL_POWER_SLEEP; + ret = dvb_usb_generic_write(d,b,3); + msleep(10); + return ret; +} +EXPORT_SYMBOL(dibusb_power_ctrl); + +int dibusb2_0_streaming_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b[2]; + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = onoff ? DIBUSB_IOCTL_CMD_ENABLE_STREAM : DIBUSB_IOCTL_CMD_DISABLE_STREAM; + + dvb_usb_generic_write(d,b,3); + + return dibusb_streaming_ctrl(d,onoff); +} +EXPORT_SYMBOL(dibusb2_0_streaming_ctrl); + +int dibusb2_0_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + if (onoff) { + u8 b[3] = { DIBUSB_REQ_SET_IOCTL, DIBUSB_IOCTL_CMD_POWER_MODE, DIBUSB_IOCTL_POWER_WAKEUP }; + return dvb_usb_generic_write(d,b,3); + } else + return 0; +} +EXPORT_SYMBOL(dibusb2_0_power_ctrl); + +static int dibusb_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ + /* write only ? */ + int wo = (rbuf == NULL || rlen == 0), + len = 2 + wlen + (wo ? 0 : 2); + + sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; + sndbuf[1] = (addr << 1) | (wo ? 0 : 1); + + memcpy(&sndbuf[2],wbuf,wlen); + + if (!wo) { + sndbuf[wlen+2] = (rlen >> 8) & 0xff; + sndbuf[wlen+3] = rlen & 0xff; + } + + return dvb_usb_generic_rw(d,sndbuf,len,rbuf,rlen,0); +} + +/* + * I2C master xfer function + */ +static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (down_interruptible(&d->i2c_sem) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else + if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) + break; + } + + up(&d->i2c_sem); + return i; +} + +static u32 dibusb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +struct i2c_algorithm dibusb_i2c_algo = { + .name = "DiBcom USB I2C algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = dibusb_i2c_xfer, + .functionality = dibusb_i2c_func, +}; +EXPORT_SYMBOL(dibusb_i2c_algo); + +int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) +{ + u8 wbuf[1] = { offs }; + return dibusb_i2c_msg(d, 0x50, wbuf, 1, val, 1); +} +EXPORT_SYMBOL(dibusb_read_eeprom_byte); + +int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *d) +{ + struct dib3000_config demod_cfg; + struct dibusb_state *st = d->priv; + + demod_cfg.pll_set = dvb_usb_pll_set_i2c; + demod_cfg.pll_init = dvb_usb_pll_init_i2c; + + for (demod_cfg.demod_address = 0x8; demod_cfg.demod_address < 0xd; demod_cfg.demod_address++) + if ((d->fe = dib3000mc_attach(&demod_cfg,&d->i2c_adap,&st->ops)) != NULL) { + d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl; + return 0; + } + + return -ENODEV; +} +EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach); + +int dibusb_dib3000mc_tuner_attach (struct dvb_usb_device *d) +{ + d->pll_addr = 0x60; + d->pll_desc = &dvb_pll_env57h1xd5; + return 0; +} +EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); + +/* + * common remote control stuff + */ +struct dvb_usb_rc_key dibusb_rc_keys[] = { + /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ + { 0x00, 0x16, KEY_POWER }, + { 0x00, 0x10, KEY_MUTE }, + { 0x00, 0x03, KEY_1 }, + { 0x00, 0x01, KEY_2 }, + { 0x00, 0x06, KEY_3 }, + { 0x00, 0x09, KEY_4 }, + { 0x00, 0x1d, KEY_5 }, + { 0x00, 0x1f, KEY_6 }, + { 0x00, 0x0d, KEY_7 }, + { 0x00, 0x19, KEY_8 }, + { 0x00, 0x1b, KEY_9 }, + { 0x00, 0x15, KEY_0 }, + { 0x00, 0x05, KEY_CHANNELUP }, + { 0x00, 0x02, KEY_CHANNELDOWN }, + { 0x00, 0x1e, KEY_VOLUMEUP }, + { 0x00, 0x0a, KEY_VOLUMEDOWN }, + { 0x00, 0x11, KEY_RECORD }, + { 0x00, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x00, 0x14, KEY_PLAY }, + { 0x00, 0x1a, KEY_STOP }, + { 0x00, 0x40, KEY_REWIND }, + { 0x00, 0x12, KEY_FASTFORWARD }, + { 0x00, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x00, 0x4c, KEY_PAUSE }, + { 0x00, 0x4d, KEY_SCREEN }, /* Full screen mode. */ + { 0x00, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ + { 0x00, 0x0c, KEY_CANCEL }, /* Cancel */ + { 0x00, 0x1c, KEY_EPG }, /* EPG */ + { 0x00, 0x00, KEY_TAB }, /* Tab */ + { 0x00, 0x48, KEY_INFO }, /* Preview */ + { 0x00, 0x04, KEY_LIST }, /* RecordList */ + { 0x00, 0x0f, KEY_TEXT }, /* Teletext */ + /* Key codes for the KWorld/ADSTech/JetWay remote. */ + { 0x86, 0x12, KEY_POWER }, + { 0x86, 0x0f, KEY_SELECT }, /* source */ + { 0x86, 0x0c, KEY_UNKNOWN }, /* scan */ + { 0x86, 0x0b, KEY_EPG }, + { 0x86, 0x10, KEY_MUTE }, + { 0x86, 0x01, KEY_1 }, + { 0x86, 0x02, KEY_2 }, + { 0x86, 0x03, KEY_3 }, + { 0x86, 0x04, KEY_4 }, + { 0x86, 0x05, KEY_5 }, + { 0x86, 0x06, KEY_6 }, + { 0x86, 0x07, KEY_7 }, + { 0x86, 0x08, KEY_8 }, + { 0x86, 0x09, KEY_9 }, + { 0x86, 0x0a, KEY_0 }, + { 0x86, 0x18, KEY_ZOOM }, + { 0x86, 0x1c, KEY_UNKNOWN }, /* preview */ + { 0x86, 0x13, KEY_UNKNOWN }, /* snap */ + { 0x86, 0x00, KEY_UNDO }, + { 0x86, 0x1d, KEY_RECORD }, + { 0x86, 0x0d, KEY_STOP }, + { 0x86, 0x0e, KEY_PAUSE }, + { 0x86, 0x16, KEY_PLAY }, + { 0x86, 0x11, KEY_BACK }, + { 0x86, 0x19, KEY_FORWARD }, + { 0x86, 0x14, KEY_UNKNOWN }, /* pip */ + { 0x86, 0x15, KEY_ESC }, + { 0x86, 0x1a, KEY_UP }, + { 0x86, 0x1e, KEY_DOWN }, + { 0x86, 0x1f, KEY_LEFT }, + { 0x86, 0x1b, KEY_RIGHT }, +}; +EXPORT_SYMBOL(dibusb_rc_keys); + +int dibusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5],cmd = DIBUSB_REQ_POLL_REMOTE; + dvb_usb_generic_rw(d,&cmd,1,key,5,0); + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} +EXPORT_SYMBOL(dibusb_rc_query); diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c new file mode 100644 index 000000000000..a0ffbb59fa14 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -0,0 +1,316 @@ +/* DVB USB compliant linux driver for mobile DVB-T USB devices based on + * reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-B) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DiBcom, which has + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_device *d) +{ + struct dib3000_config demod_cfg; + struct dibusb_state *st = d->priv; + + demod_cfg.demod_address = 0x8; + demod_cfg.pll_set = dvb_usb_pll_set_i2c; + demod_cfg.pll_init = dvb_usb_pll_init_i2c; + + if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL) + return -ENODEV; + + d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl; + + return 0; +} + +/* some of the dibusb 1.1 device aren't equipped with the default tuner + * (Thomson Cable), but with a Panasonic ENV77H11D5. This function figures + * this out. */ +static int dibusb_dib3000mb_tuner_attach (struct dvb_usb_device *d) +{ + u8 b[2] = { 0,0 }, b2[1]; + int ret = 0; + struct i2c_msg msg[2] = { + { .flags = 0, .buf = b, .len = 2 }, + { .flags = I2C_M_RD, .buf = b2, .len = 1 }, + }; + + /* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */ + msg[0].addr = msg[1].addr = 0x60; + + if (d->tuner_pass_ctrl) + d->tuner_pass_ctrl(d->fe,1,msg[0].addr); + + if (i2c_transfer (&d->i2c_adap, msg, 2) != 2) { + err("tuner i2c write failed."); + ret = -EREMOTEIO; + } + + if (d->tuner_pass_ctrl) + d->tuner_pass_ctrl(d->fe,0,msg[0].addr); + + if (b2[0] == 0xfe) { + info("this device has the Thomson Cable onboard. Which is default."); + d->pll_addr = 0x61; + d->pll_desc = &dvb_pll_tua6010xs; + } else { + u8 bpll[4] = { 0x0b, 0xf5, 0x85, 0xab }; + info("this device has the Panasonic ENV77H11D5 onboard."); + d->pll_addr = 0x60; + memcpy(d->pll_init,bpll,4); + d->pll_desc = &dvb_pll_tda665x; + } + + return ret; +} + +/* USB Driver stuff */ +static struct dvb_usb_properties dibusb1_1_properties; +static struct dvb_usb_properties dibusb1_1_an2235_properties; +static struct dvb_usb_properties dibusb2_0b_properties; + +static int dibusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (dvb_usb_device_init(intf,&dibusb1_1_properties,THIS_MODULE) == 0 || + dvb_usb_device_init(intf,&dibusb1_1_an2235_properties,THIS_MODULE) == 0 || + dvb_usb_device_init(intf,&dibusb2_0b_properties,THIS_MODULE) == 0) + return 0; + + return -EINVAL; +} + +/* do not change the order of the ID table */ +static struct usb_device_id dibusb_dib3000mb_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA_UNK, USB_PID_AVERMEDIA_DVBT_USB_COLD)}, +/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA_UNK, USB_PID_AVERMEDIA_DVBT_USB_WARM)}, +/* 02 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) }, +/* 03 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) }, +/* 04 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) }, +/* 05 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) }, +/* 06 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) }, +/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) }, +/* 08 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) }, +/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) }, +/* 10 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) }, +/* 11 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) }, +/* 12 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) }, +/* 13 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) }, +/* 14 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) }, +/* 15 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_COLD) }, +/* 16 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_WARM) }, +/* 17 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) }, +/* 18 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) }, +/* 19 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) }, +/* 20 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) }, +/* 21 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) }, +/* 22 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) }, +/* 23 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) }, +/* 24 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, dibusb_dib3000mb_table); + +static struct dvb_usb_properties dibusb1_1_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_IS_AN_I2C_ADAPTER, + .pid_filter_count = 16, + + .usb_ctrl = CYPRESS_AN2135, + + .firmware = "dvb-usb-dibusb-5.0.0.11.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .power_ctrl = dibusb_power_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_dib3000mb_tuner_attach, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dibusb_rc_keys, + .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 8, + .devices = { + { "AVerMedia AverTV DVBT USB1.1", + { &dibusb_dib3000mb_table[0], NULL }, + { &dibusb_dib3000mb_table[1], NULL }, + }, + { "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)", + { &dibusb_dib3000mb_table[2], &dibusb_dib3000mb_table[4], NULL}, + { &dibusb_dib3000mb_table[3], NULL }, + }, + { "DiBcom USB1.1 DVB-T reference design (MOD3000)", + { &dibusb_dib3000mb_table[5], NULL }, + { &dibusb_dib3000mb_table[6], NULL }, + }, + { "KWorld V-Stream XPERT DTV - DVB-T USB1.1", + { &dibusb_dib3000mb_table[7], NULL }, + { &dibusb_dib3000mb_table[8], NULL }, + }, + { "Grandtec USB1.1 DVB-T", + { &dibusb_dib3000mb_table[9], &dibusb_dib3000mb_table[11], NULL }, + { &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL }, + }, + { "Unkown USB1.1 DVB-T device ???? please report the name to the author", + { &dibusb_dib3000mb_table[13], NULL }, + { &dibusb_dib3000mb_table[14], NULL }, + }, + { "TwinhanDTV USB-Ter USB1.1 / Magic Box I / HAMA USB1.1 DVB-T device", + { &dibusb_dib3000mb_table[15], &dibusb_dib3000mb_table[17], NULL}, + { &dibusb_dib3000mb_table[16], &dibusb_dib3000mb_table[18], NULL}, + }, + { "Artec T1 USB1.1 TVBOX with AN2135", + { &dibusb_dib3000mb_table[19], NULL }, + { &dibusb_dib3000mb_table[20], NULL }, + }, + } +}; + +static struct dvb_usb_properties dibusb1_1_an2235_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_AN2235, + + .firmware = "dvb-usb-dibusb-an2235-01.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .power_ctrl = dibusb_power_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_dib3000mb_tuner_attach, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dibusb_rc_keys, + .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { "Artec T1 USB1.1 TVBOX with AN2235", + { &dibusb_dib3000mb_table[20], NULL }, + { &dibusb_dib3000mb_table[21], NULL }, + }, + } +}; + +static struct dvb_usb_properties dibusb2_0b_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + + .firmware = "dvb-usb-adstech-usb2-01.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .power_ctrl = dibusb2_0_power_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_dib3000mb_tuner_attach, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dibusb_rc_keys, + .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 2, + .devices = { + { "KWorld/ADSTech Instant DVB-T USB 2.0", + { &dibusb_dib3000mb_table[23], NULL }, + { &dibusb_dib3000mb_table[24], NULL }, /* device ID with default DIBUSB2_0-firmware */ + }, + } +}; + +static struct usb_driver dibusb_driver = { + .owner = THIS_MODULE, + .name = "DiBcom based USB DVB-T devices (DiB3000M-B based)", + .probe = dibusb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dibusb_dib3000mb_table, +}; + +/* module stuff */ +static int __init dibusb_module_init(void) +{ + int result; + if ((result = usb_register(&dibusb_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit dibusb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&dibusb_driver); +} + +module_init (dibusb_module_init); +module_exit (dibusb_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for DiBcom USB DVB-T devices (DiB3000M-B based)"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c new file mode 100644 index 000000000000..aad8ed3fe005 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -0,0 +1,116 @@ +/* DVB USB compliant linux driver for mobile DVB-T USB devices based on + * reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-C/P) + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DiBcom, which has + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +/* USB Driver stuff */ +static struct dvb_usb_properties dibusb_mc_properties; + +static int dibusb_mc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&dibusb_mc_properties,THIS_MODULE); +} + +/* do not change the order of the ID table */ +static struct usb_device_id dibusb_dib3000mc_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, +/* 02 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table); + +static struct dvb_usb_properties dibusb_mc_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_IS_AN_I2C_ADAPTER, + .pid_filter_count = 32, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-dibusb-6.0.0.8.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .power_ctrl = dibusb2_0_power_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dibusb_rc_keys, + .rc_key_map_size = 63, /* FIXME */ + .rc_query = dibusb_rc_query, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 2, + .devices = { + { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", + { &dibusb_dib3000mc_table[0], NULL }, + { &dibusb_dib3000mc_table[1], NULL }, + }, + { "Artec T1 USB2.0 TVBOX (please report the warm ID)", + { &dibusb_dib3000mc_table[2], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver dibusb_mc_driver = { + .owner = THIS_MODULE, + .name = "DiBcom based USB2.0 DVB-T (DiB3000M-C/P based) devices", + .probe = dibusb_mc_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dibusb_dib3000mc_table, +}; + +/* module stuff */ +static int __init dibusb_mc_module_init(void) +{ + int result; + if ((result = usb_register(&dibusb_mc_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit dibusb_mc_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&dibusb_mc_driver); +} + +module_init (dibusb_mc_module_init); +module_exit (dibusb_mc_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for DiBcom USB2.0 DVB-T (DiB3000M-C/P based) devices"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h new file mode 100644 index 000000000000..6611f62977c0 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dibusb.h @@ -0,0 +1,122 @@ +/* Header file for all dibusb-based-receivers. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_DIBUSB_H_ +#define _DVB_USB_DIBUSB_H_ + +#define DVB_USB_LOG_PREFIX "dibusb" +#include "dvb-usb.h" + +#include "dib3000.h" + +/* + * protocol of all dibusb related devices + */ + +/* + * bulk msg to/from endpoint 0x01 + * + * general structure: + * request_byte parameter_bytes + */ + +#define DIBUSB_REQ_START_READ 0x00 +#define DIBUSB_REQ_START_DEMOD 0x01 + +/* + * i2c read + * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word + * bulk read: byte_buffer (length_word bytes) + */ +#define DIBUSB_REQ_I2C_READ 0x02 + +/* + * i2c write + * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes + */ +#define DIBUSB_REQ_I2C_WRITE 0x03 + +/* + * polling the value of the remote control + * bulk write: 0x04 + * bulk read: byte_buffer (5 bytes) + */ +#define DIBUSB_REQ_POLL_REMOTE 0x04 + +/* additional status values for Hauppauge Remote Control Protocol */ +#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01 +#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03 + +/* streaming mode: + * bulk write: 0x05 mode_byte + * + * mode_byte is mostly 0x00 + */ +#define DIBUSB_REQ_SET_STREAMING_MODE 0x05 + +/* interrupt the internal read loop, when blocking */ +#define DIBUSB_REQ_INTR_READ 0x06 + +/* io control + * 0x07 cmd_byte param_bytes + * + * param_bytes can be up to 32 bytes + * + * cmd_byte function parameter name + * 0x00 power mode + * 0x00 sleep + * 0x01 wakeup + * + * 0x01 enable streaming + * 0x02 disable streaming + * + * + */ +#define DIBUSB_REQ_SET_IOCTL 0x07 + +/* IOCTL commands */ + +/* change the power mode in firmware */ +#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00 +#define DIBUSB_IOCTL_POWER_SLEEP 0x00 +#define DIBUSB_IOCTL_POWER_WAKEUP 0x01 + +/* modify streaming of the FX2 */ +#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01 +#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02 + +struct dibusb_state { + struct dib_fe_xfer_ops ops; + + /* for RC5 remote control */ + int old_toggle; + int last_repeat_count; +}; + +extern struct i2c_algorithm dibusb_i2c_algo; + +extern int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *); +extern int dibusb_dib3000mc_tuner_attach (struct dvb_usb_device *); + +extern int dibusb_streaming_ctrl(struct dvb_usb_device *, int); +extern int dibusb_pid_filter(struct dvb_usb_device *, int, u16, int); +extern int dibusb_pid_filter_ctrl(struct dvb_usb_device *, int); +extern int dibusb_power_ctrl(struct dvb_usb_device *, int); +extern int dibusb2_0_streaming_ctrl(struct dvb_usb_device *, int); +extern int dibusb2_0_power_ctrl(struct dvb_usb_device *, int); + +#define DEFAULT_RC_INTERVAL 150 +//#define DEFAULT_RC_INTERVAL 100000 + +extern struct dvb_usb_rc_key dibusb_rc_keys[]; +extern int dibusb_rc_query(struct dvb_usb_device *, u32 *, int *); +extern int dibusb_read_eeprom_byte(struct dvb_usb_device *, u8, u8 *); + +#endif diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c new file mode 100644 index 000000000000..5acf3fde9522 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -0,0 +1,282 @@ +/* DVB USB compliant linux driver for Nebula Electronics uDigiTV DVB-T USB2.0 + * receiver + * + * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) and + * Allan Third (allan.third@cs.man.ac.uk) + * + * partly based on the SDK published by Nebula Electronics (TODO do we want this line ?) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "digitv.h" + +#include "mt352.h" +#include "nxt6000.h" + +/* debug */ +int dvb_usb_digitv_debug; +module_param_named(debug,dvb_usb_digitv_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +static int digitv_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 sndbuf[7],rcvbuf[7]; + memset(sndbuf,0,7); memset(rcvbuf,0,7); + + sndbuf[0] = cmd; + sndbuf[1] = vv; + sndbuf[2] = wo ? wlen : rlen; + + if (!wo) { + memcpy(&sndbuf[3],wbuf,wlen); + dvb_usb_generic_write(d,sndbuf,7); + } else { + dvb_usb_generic_rw(d,sndbuf,7,rcvbuf,7,10); + memcpy(&rbuf,&rcvbuf[3],rlen); + } + return 0; +} + +/* I2C */ +static int digitv_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (down_interruptible(&d->i2c_sem) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (digitv_ctrl_msg(d, USB_READ_COFDM, msg[i].buf[0], NULL, 0, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else + if (digitv_ctrl_msg(d,USB_WRITE_COFDM, msg[i].buf[0], + &msg[i].buf[1],msg[i].len-1,NULL,0) < 0) + break; + } + + up(&d->i2c_sem); + return i; +} + +static u32 digitv_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm digitv_i2c_algo = { + .name = "Nebula DigiTV USB I2C algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = digitv_i2c_xfer, + .functionality = digitv_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int digitv_identify_state (struct usb_device *udev, struct + dvb_usb_properties *props, struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; + return 0; +} + +static int digitv_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 mt352_clock_config[] = { 0x89, 0x38, 0x2d }; + static u8 mt352_reset[] = { 0x50, 0x80 }; + static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 }; + + static u8 mt352_agc_cfg[] = { 0x68, 0xa0 }; + static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0xa0 }; + static u8 mt352_acq_ctl[] = { 0x53, 0x50 }; + static u8 mt352_agc_target[] = { 0x67, 0x20 }; + + static u8 mt352_rs_err_per[] = { 0x7c, 0x00, 0x01 }; + static u8 mt352_snr_select[] = { 0x79, 0x00, 0x20 }; + + static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x05 }; + + static u8 mt352_scan_ctl[] = { 0x88, 0x0f }; + static u8 mt352_capt_range[] = { 0x75, 0x32 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + msleep(1); + mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio)); + + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl)); + mt352_write(fe, mt352_agc_target, sizeof(mt352_agc_target)); + + + mt352_write(fe, mt352_rs_err_per, sizeof(mt352_rs_err_per)); + mt352_write(fe, mt352_snr_select, sizeof(mt352_snr_select)); + + mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1)); + + mt352_write(fe, mt352_scan_ctl, sizeof(mt352_scan_ctl)); + mt352_write(fe, mt352_capt_range, sizeof(mt352_capt_range)); + + return 0; +} + +static struct mt352_config digitv_mt352_config = { + .demod_address = 0x0, /* ignored by the digitv anyway */ + .demod_init = digitv_mt352_demod_init, + .pll_set = NULL, /* TODO */ +}; + +static struct nxt6000_config digitv_nxt6000_config = { + .demod_address = 0x0, /* ignored by the digitv anyway */ + .clock_inversion = 0x0, + + .pll_init = NULL, + .pll_set = NULL, +}; + +static int digitv_frontend_attach(struct dvb_usb_device *d) +{ + if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) == NULL) + return 0; + if ((d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) == NULL) { + + warn("nxt6000 support is not done yet, in fact you are one of the first " + "person who wants to use this device in Linux. Please report to " + "linux-dvb@linuxtv.org"); + + return 0; + } + return -EIO; +} + +static struct dvb_usb_rc_key digitv_rc_keys[] = { + { 0x00, 0x16, KEY_POWER }, /* dummy key */ +}; + +/* TODO is it really the NEC protocol ? */ +int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5]; + + digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4); + /* TODO state, maybe it is VV ? */ + if (key[1] != 0) + key[0] = 0x01; /* if something is inside the buffer, simulate key press */ + + /* call the universal NEC remote processor, to find out the key's state and event */ + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} + + +/* DVB USB Driver stuff */ +static struct dvb_usb_properties digitv_properties; + +static int digitv_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&digitv_properties,THIS_MODULE); +} + +static struct usb_device_id digitv_table [] = { + { USB_DEVICE(USB_VID_ANCHOR, USB_PID_NEBULA_DIGITV) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, digitv_table); + +static struct dvb_usb_properties digitv_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-digitv-01.fw", + + .size_of_priv = 0, + + .streaming_ctrl = NULL, + .pid_filter = NULL, + .pid_filter_ctrl = NULL, + .power_ctrl = NULL, + .frontend_attach = digitv_frontend_attach, + .tuner_attach = NULL, // digitv_tuner_attach, + .read_mac_address = NULL, + + .rc_interval = 1000, + .rc_key_map = digitv_rc_keys, + .rc_key_map_size = ARRAY_SIZE(digitv_rc_keys), + .rc_query = digitv_rc_query, + + .identify_state = digitv_identify_state, + + .i2c_algo = &digitv_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 2, + .devices = { + { "Nebula Electronics uDigiTV DVB-T USB2.0)", + { &digitv_table[0], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver digitv_driver = { + .owner = THIS_MODULE, + .name = "Nebula Electronics uDigiTV DVB-T USB2.0 device", + .probe = digitv_probe, + .disconnect = dvb_usb_device_exit, + .id_table = digitv_table, +}; + +/* module stuff */ +static int __init digitv_module_init(void) +{ + int result; + if ((result = usb_register(&digitv_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit digitv_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&digitv_driver); +} + +module_init (digitv_module_init); +module_exit (digitv_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Nebula Electronics uDigiTV DVB-T USB2.0"); +MODULE_VERSION("1.0-alpha"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/digitv.h b/drivers/media/dvb/dvb-usb/digitv.h new file mode 100644 index 000000000000..477ee428a70e --- /dev/null +++ b/drivers/media/dvb/dvb-usb/digitv.h @@ -0,0 +1,65 @@ +#ifndef _DVB_USB_DIGITV_H_ +#define _DVB_USB_DIGITV_H_ + +#define DVB_USB_LOG_PREFIX "digitv" +#include "dvb-usb.h" + +extern int dvb_usb_digitv_debug; +#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args) + +/* protocol (from usblogging and the SDK: + * + * Always 7 bytes bulk message(s) for controlling + * + * First byte describes the command. Reads are 2 consecutive transfer (as always). + * + * General structure: + * + * write or first message of a read: + * VV B0 B1 B2 B3 + * + * second message of a read + * VV R0 R1 R2 R3 + * + * whereas 0 < len <= 4 + * + * I2C address is stored somewhere inside the device. + * + * 0x01 read from EEPROM + * VV = offset; B* = 0; R* = value(s) + * + * 0x02 read register of the COFDM + * VV = register; B* = 0; R* = value(s) + * + * 0x05 write register of the COFDM + * VV = register; B* = value(s); + * + * 0x06 write to the tuner (only for NXT6000) + * VV = 0; B* = PLL data; len = 4; + * + * 0x03 read remote control + * VV = 0; B* = 0; len = 4; R* = key + * + * 0x07 write to the remote (don't know why one should this, resetting ?) + * VV = 0; B* = key; len = 4; + * + * 0x08 write remote type + * VV = 0; B[0] = 0x01, len = 4 + * + * 0x09 write device init + * TODO + */ +#define USB_READ_EEPROM 1 + +#define USB_READ_COFDM 2 +#define USB_WRITE_COFDM 5 + +#define USB_WRITE_TUNER 6 + +#define USB_READ_REMOTE 3 +#define USB_WRITE_REMOTE 7 +#define USB_WRITE_REMOTE_TYPE 8 + +#define USB_DEV_INIT 9 + +#endif diff --git a/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u-fe.c similarity index 61% rename from drivers/media/dvb/dibusb/dvb-fe-dtt200u.c rename to drivers/media/dvb/dvb-usb/dtt200u-fe.c index 1872aa6d200a..d17d768038c6 100644 --- a/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u-fe.c @@ -1,42 +1,18 @@ -/* - * dvb-dtt200u-fe.c is a driver which implements the frontend-part of the - * Yakumo/Typhoon/Hama USB2.0 boxes. It is hard-wired to the dibusb-driver as - * it uses the usb-transfer functions directly (maybe creating a - * generic-dvb-usb-lib for all usb-drivers will be reduce some more code.) +/* Frontend part of the Linux driver for the Yakumo/Hama/Typhoon DVB-T + * USB2.0 receiver. * * Copyright (C) 2005 Patrick Boettcher * - * see dvb-dibusb-core.c for copyright details. - */ - -/* guessed protocol description (reverse engineered): - * read - * 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1 - * 81 - - * 82 - crash - do not touch - * 83 - crash - do not touch - * 84 - remote control - * 85 - crash - do not touch (OK, stop testing here) - * 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal) - * 89 - noise-to-signal - * 8a - unkown 1 byte - signal_strength - * 8c - ber ??? - * 8d - ber - * 8e - unc + * 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, version 2. * - * write - * 02 - bandwidth - * 03 - frequency (divided by 250000) - * 04 - pid table (index pid(7:0) pid(12:8)) - * 05 - reset the pid table - * 08 - demod transfer enabled or not (FX2 transfer is enabled by default) + * see Documentation/dvb/README.dvb-usb for more information */ - -#include "dvb-dibusb.h" -#include "dvb_frontend.h" +#include "dtt200u.h" struct dtt200u_fe_state { - struct usb_dibusb *dib; + struct dvb_usb_device *d; struct dvb_frontend_parameters fep; struct dvb_frontend frontend; @@ -47,11 +23,11 @@ struct dtt200u_fe_state { static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) { struct dtt200u_fe_state *state = fe->demodulator_priv; - u8 bw[1] = { 0x81 }; + u8 bw = GET_TUNE_STAT; u8 br[3] = { 0 }; // u8 bdeb[5] = { 0 }; - dibusb_readwrite_usb(state->dib,bw,1,br,3); + dvb_usb_generic_rw(state->d,&bw,1,br,3,0); switch (br[0]) { case 0x01: *stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; @@ -60,12 +36,12 @@ static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) *stat = 0; break; default: - moan("br[0]",0x81); + moan("br[0]",GET_TUNE_STAT); break; } // bw[0] = 0x88; -// dibusb_readwrite_usb(state->dib,bw,1,bdeb,5); +// dvb_usb_generic_rw(state->d,bw,1,bdeb,5,0); // deb_info("%02x: %02x %02x %02x %02x %02x\n",bw[0],bdeb[0],bdeb[1],bdeb[2],bdeb[3],bdeb[4]); @@ -74,27 +50,26 @@ static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber) { struct dtt200u_fe_state *state = fe->demodulator_priv; - u8 bw[1] = { 0x8d }; + u8 bw = GET_BER; *ber = 0; - dibusb_readwrite_usb(state->dib,bw,1,(u8*) ber, 3); + dvb_usb_generic_rw(state->d,&bw,1,(u8*) ber,3,0); return 0; } static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) { struct dtt200u_fe_state *state = fe->demodulator_priv; - u8 bw[1] = { 0x8c }; + u8 bw = GET_UNK; *unc = 0; - dibusb_readwrite_usb(state->dib,bw,1,(u8*) unc, 3); + dvb_usb_generic_rw(state->d,&bw,1,(u8*) unc,3,0); return 0; } static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) { struct dtt200u_fe_state *state = fe->demodulator_priv; - u8 bw[1] = { 0x8a }; - u8 b; - dibusb_readwrite_usb(state->dib,bw,1,&b, 1); + u8 bw = GET_SIG_STRENGTH, b; + dvb_usb_generic_rw(state->d,&bw,1,&b,1,0); *strength = (b << 8) | b; return 0; } @@ -102,18 +77,17 @@ static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strengt static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr) { struct dtt200u_fe_state *state = fe->demodulator_priv; - u8 bw[1] = { 0x89 }; - u8 br[1] = { 0 }; - dibusb_readwrite_usb(state->dib,bw,1,br,1); - *snr = ((0xff - br[0]) << 8) | (0xff - br[0]); + u8 bw = GET_SNR,br; + dvb_usb_generic_rw(state->d,&bw,1,&br,1,0); + *snr = ~((br << 8) | br); return 0; } static int dtt200u_fe_init(struct dvb_frontend* fe) { struct dtt200u_fe_state *state = fe->demodulator_priv; - u8 b[] = { 0x01 }; - return dibusb_write_usb(state->dib,b,1); + u8 b = RESET_DEMOD; + return dvb_usb_generic_write(state->d,&b,1); } static int dtt200u_fe_sleep(struct dvb_frontend* fe) @@ -134,7 +108,7 @@ static int dtt200u_fe_set_frontend(struct dvb_frontend* fe, { struct dtt200u_fe_state *state = fe->demodulator_priv; u16 freq = fep->frequency / 250000; - u8 bw,bwbuf[2] = { 0x03, 0 }, freqbuf[3] = { 0x02, 0, 0 }; + u8 bw,bwbuf[2] = { SET_BANDWIDTH, 0 }, freqbuf[3] = { SET_FREQUENCY, 0, 0 }; switch (fep->u.ofdm.bandwidth) { case BANDWIDTH_8_MHZ: bw = 8; break; @@ -147,11 +121,11 @@ static int dtt200u_fe_set_frontend(struct dvb_frontend* fe, deb_info("set_frontend\n"); bwbuf[1] = bw; - dibusb_write_usb(state->dib,bwbuf,2); + dvb_usb_generic_write(state->d,bwbuf,2); freqbuf[1] = freq & 0xff; freqbuf[2] = (freq >> 8) & 0xff; - dibusb_write_usb(state->dib,freqbuf,3); + dvb_usb_generic_write(state->d,freqbuf,3); memcpy(&state->fep,fep,sizeof(struct dvb_frontend_parameters)); @@ -172,37 +146,9 @@ static void dtt200u_fe_release(struct dvb_frontend* fe) kfree(state); } -static int dtt200u_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff) -{ - struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; - u8 b_pid[4]; - pid = onoff ? pid : 0; - - b_pid[0] = 0x04; - b_pid[1] = index; - b_pid[2] = pid & 0xff; - b_pid[3] = (pid >> 8) & 0xff; - - dibusb_write_usb(state->dib,b_pid,4); - return 0; -} - -static int dtt200u_fifo_control(struct dvb_frontend *fe, int onoff) -{ - struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; - u8 b_streaming[2] = { 0x08, onoff }; - u8 b_rst_pid[1] = { 0x05 }; - - dibusb_write_usb(state->dib,b_streaming,2); - - if (!onoff) - dibusb_write_usb(state->dib,b_rst_pid,1); - return 0; -} - static struct dvb_frontend_ops dtt200u_fe_ops; -struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *dib, struct dib_fe_xfer_ops *xfer_ops) +struct dvb_frontend* dtt200u_fe_attach(struct dvb_usb_device *d) { struct dtt200u_fe_state* state = NULL; @@ -214,14 +160,11 @@ struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *dib, struct dib_fe_xfe deb_info("attaching frontend dtt200u\n"); - state->dib = dib; + state->d = d; state->frontend.ops = &dtt200u_fe_ops; state->frontend.demodulator_priv = state; - xfer_ops->fifo_ctrl = dtt200u_fifo_control; - xfer_ops->pid_ctrl = dtt200u_pid_control; - goto success; error: return NULL; diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c new file mode 100644 index 000000000000..fb2b5a2da137 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -0,0 +1,171 @@ +/* DVB USB library compliant Linux driver for the Yakumo/Hama/Typhoon DVB-T + * USB2.0 receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dtt200u.h" + +/* debug */ +int dvb_usb_dtt200u_debug; +module_param_named(debug,dvb_usb_dtt200u_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS); + +static int dtt200u_streaming_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b_streaming[2] = { SET_TS_CTRL, onoff }; + u8 b_rst_pid = RESET_PID_FILTER; + + dvb_usb_generic_write(d,b_streaming,2); + + if (!onoff) + dvb_usb_generic_write(d,&b_rst_pid,1); + return 0; +} + +static int dtt200u_pid_filter(struct dvb_usb_device *d, int index, u16 pid, int onoff) +{ + u8 b_pid[4]; + pid = onoff ? pid : 0; + + b_pid[0] = SET_PID_FILTER; + b_pid[1] = index; + b_pid[2] = pid & 0xff; + b_pid[3] = (pid >> 8) & 0xff; + + return dvb_usb_generic_write(d,b_pid,4); +} + +/* remote control */ +/* key list for the tiny remote control (Yakumo, don't know about the others) */ +static struct dvb_usb_rc_key dtt200u_rc_keys[] = { + { 0x80, 0x01, KEY_MUTE }, + { 0x80, 0x02, KEY_CHANNELDOWN }, + { 0x80, 0x03, KEY_VOLUMEDOWN }, + { 0x80, 0x04, KEY_1 }, + { 0x80, 0x05, KEY_2 }, + { 0x80, 0x06, KEY_3 }, + { 0x80, 0x07, KEY_4 }, + { 0x80, 0x08, KEY_5 }, + { 0x80, 0x09, KEY_6 }, + { 0x80, 0x0a, KEY_7 }, + { 0x00, 0x0c, KEY_ZOOM }, + { 0x80, 0x0d, KEY_0 }, + { 0x00, 0x0e, KEY_SELECT }, + { 0x80, 0x12, KEY_POWER }, + { 0x80, 0x1a, KEY_CHANNELUP }, + { 0x80, 0x1b, KEY_8 }, + { 0x80, 0x1e, KEY_VOLUMEUP }, + { 0x80, 0x1f, KEY_9 }, +}; + +static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5],cmd = GET_RC_KEY; + dvb_usb_generic_rw(d,&cmd,1,key,5,0); + dvb_usb_nec_rc_key_to_event(d,key,event,state); + if (key[0] != 0) + deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); + return 0; +} + +static int dtt200u_frontend_attach(struct dvb_usb_device *d) +{ + d->fe = dtt200u_fe_attach(d); + return 0; +} + +static struct dvb_usb_properties dtt200u_properties; + +static int dtt200u_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE); +} + +static struct usb_device_id dtt200u_usb_table [] = { + { USB_DEVICE(USB_VID_AVERMEDIA_UNK, USB_PID_DTT200U_COLD) }, + { USB_DEVICE(USB_VID_AVERMEDIA_UNK, USB_PID_DTT200U_WARM) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); + +static struct dvb_usb_properties dtt200u_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, + .pid_filter_count = 255, /* It is a guess, but there are at least 10 */ + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-dtt200u-01.fw", + + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + + .rc_interval = 200, + .rc_key_map = dtt200u_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_query = dtt200u_rc_query, + + .generic_bulk_ctrl_endpoint = 0x01, + + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { .name = "Yakumo/Hama/Typhoon DVB-T USB2.0)", + .cold_ids = { &dtt200u_usb_table[0], &dtt200u_usb_table[2] }, + .warm_ids = { &dtt200u_usb_table[1], NULL }, + }, + { 0 }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver dtt200u_usb_driver = { + .owner = THIS_MODULE, + .name = "Yakumo/Hama/Typhoon DVB-T USB2.0", + .probe = dtt200u_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dtt200u_usb_table, +}; + +/* module stuff */ +static int __init dtt200u_usb_module_init(void) +{ + int result; + if ((result = usb_register(&dtt200u_usb_driver))) { + err("usb_register failed. (%d)",result); + return result; + } + + return 0; +} + +static void __exit dtt200u_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&dtt200u_usb_driver); +} + +module_init(dtt200u_usb_module_init); +module_exit(dtt200u_usb_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for the Yakumo/Hama/Typhoon DVB-T USB2.0 device"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dtt200u.h b/drivers/media/dvb/dvb-usb/dtt200u.h new file mode 100644 index 000000000000..ed4142071518 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dtt200u.h @@ -0,0 +1,66 @@ +/* Common header file of Linux driver for the Yakumo/Hama/Typhoon DVB-T + * USB2.0 receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_DTT200U_H_ +#define _DVB_USB_DTT200U_H_ + +#define DVB_USB_LOG_PREFIX "dtt200u" +#include "dvb-usb.h" + +extern int dvb_usb_dtt200u_debug; +#define deb_info(args...) dprintk(dvb_usb_dtt200u_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_dtt200u_debug,0x02,args) + +/* guessed protocol description (reverse engineered): + * read + * 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1 + * 81 - + * 82 - crash - do not touch + * 83 - crash - do not touch + * 84 - remote control + * 85 - crash - do not touch (OK, stop testing here) + * 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal) + * 89 - noise-to-signal + * 8a - unkown 1 byte - signal_strength + * 8c - ber ??? + * 8d - ber + * 8e - unc + */ + +#define GET_SPEED 0x00 +#define GET_TUNE_STAT 0x81 +#define GET_RC_KEY 0x84 +#define GET_STATUS 0x88 +#define GET_SNR 0x89 +#define GET_SIG_STRENGTH 0x8a +#define GET_UNK 0x8c +#define GET_BER 0x8d +#define GET_UNC 0x8e + +/* write + * 01 - reset the demod + * 02 - frequency (divided by 250000) + * 03 - bandwidth + * 04 - pid table (index pid(7:0) pid(12:8)) + * 05 - reset the pid table + * 08 - demod transfer enabled or not (FX2 transfer is enabled by default) + */ + +#define RESET_DEMOD 0x01 +#define SET_FREQUENCY 0x02 +#define SET_BANDWIDTH 0x03 +#define SET_PID_FILTER 0x04 +#define RESET_PID_FILTER 0x05 +#define SET_TS_CTRL 0x08 + +extern struct dvb_frontend * dtt200u_fe_attach(struct dvb_usb_device *d); + +#endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-common.h b/drivers/media/dvb/dvb-usb/dvb-usb-common.h new file mode 100644 index 000000000000..67e0d73fbceb --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-common.h @@ -0,0 +1,44 @@ +/* dvb-usb-common.h is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * a header file containing prototypes and types for internal use of the dvb-usb-lib + */ +#ifndef _DVB_USB_COMMON_H_ +#define _DVB_USB_COMMON_H_ + +#define DVB_USB_LOG_PREFIX "dvb-usb" +#include "dvb-usb.h" + +extern int dvb_usb_debug; + +#define deb_info(args...) dprintk(dvb_usb_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_debug,0x02,args) +#define deb_pll(args...) dprintk(dvb_usb_debug,0x04,args) +#define deb_ts(args...) dprintk(dvb_usb_debug,0x08,args) +#define deb_err(args...) dprintk(dvb_usb_debug,0x10,args) +#define deb_rc(args...) dprintk(dvb_usb_debug,0x20,args) +#define deb_fw(args...) dprintk(dvb_usb_debug,0x40,args) + +/* commonly used methods */ +extern int usb_cypress_load_firmware(struct usb_device *, const char *, int); + +extern int dvb_usb_urb_submit(struct dvb_usb_device *); +extern int dvb_usb_urb_kill(struct dvb_usb_device *); +extern int dvb_usb_urb_init(struct dvb_usb_device *); +extern int dvb_usb_urb_exit(struct dvb_usb_device *); + +extern int dvb_usb_i2c_init(struct dvb_usb_device *); +extern int dvb_usb_i2c_exit(struct dvb_usb_device *); + +extern int dvb_usb_dvb_init(struct dvb_usb_device *); +extern int dvb_usb_dvb_exit(struct dvb_usb_device *); + +extern int dvb_usb_fe_init(struct dvb_usb_device *); +extern int dvb_usb_fe_exit(struct dvb_usb_device *); + +extern int dvb_usb_remote_init(struct dvb_usb_device *); +extern int dvb_usb_remote_exit(struct dvb_usb_device *); + +#endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c new file mode 100644 index 000000000000..bdd72f779707 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -0,0 +1,210 @@ +/* dvb-usb-dvb.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing and handling the + * linux-dvb API. + */ +#include "dvb-usb-common.h" + +static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct dvb_usb_device *d = dvbdmxfeed->demux->priv; + int newfeedcount,ret; + + if (d == NULL) + return -ENODEV; + + newfeedcount = d->feedcount + (onoff ? 1 : -1); + + /* + * stop feed before setting a new pid if there will be no pid anymore + */ + if (newfeedcount == 0) { + deb_ts("stop feeding\n"); + + if (d->props.streaming_ctrl != NULL) + if ((ret = d->props.streaming_ctrl(d,0))) + err("error while stopping stream."); + + dvb_usb_urb_kill(d); + } + + d->feedcount = newfeedcount; + + /* activate the pid on the device specific pid_filter */ + deb_ts("setting pid: %5d %04x at index %d '%s'\n",dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? "on" : "off"); + if (d->props.caps & DVB_USB_HAS_PID_FILTER && + d->pid_filtering && + d->props.pid_filter != NULL) + d->props.pid_filter(d,dvbdmxfeed->index,dvbdmxfeed->pid,onoff); + + /* start the feed if this was the first feed and there is still a feed + * for reception. + */ + if (d->feedcount == onoff && d->feedcount > 0) { + + deb_ts("controlling pid parser\n"); + if (d->props.caps & DVB_USB_HAS_PID_FILTER && + d->props.caps & DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF && + d->props.pid_filter_ctrl != NULL) + if (d->props.pid_filter_ctrl(d,d->pid_filtering) < 0) + err("could not handle pid_parser"); + + deb_ts("start feeding\n"); + if (d->props.streaming_ctrl != NULL) + if (d->props.streaming_ctrl(d,1)) { + err("error while enabling fifo."); + return -ENODEV; + } + + dvb_usb_urb_submit(d); + } + return 0; +} + +static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + return dvb_usb_ctrl_feed(dvbdmxfeed,1); +} + +static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type); + return dvb_usb_ctrl_feed(dvbdmxfeed,0); +} + +int dvb_usb_dvb_init(struct dvb_usb_device *d) +{ + int ret; + + if ((ret = dvb_register_adapter(&d->dvb_adap, d->desc->name, + d->owner)) < 0) { + deb_info("dvb_register_adapter failed: error %d", ret); + goto err; + } + d->dvb_adap.priv = d; + + if (d->props.read_mac_address) { + if (d->props.read_mac_address(d,d->dvb_adap.proposed_mac) == 0) + info("MAC address: %02x:%02x:%02x:%02x:%02x:%02x",d->dvb_adap.proposed_mac[0], + d->dvb_adap.proposed_mac[1],d->dvb_adap.proposed_mac[2], + d->dvb_adap.proposed_mac[3],d->dvb_adap.proposed_mac[4], + d->dvb_adap.proposed_mac[5]); + else + err("MAC address reading failed."); + } + + + d->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + d->demux.priv = d; + + d->demux.feednum = d->demux.filternum = d->max_feed_count; + d->demux.start_feed = dvb_usb_start_feed; + d->demux.stop_feed = dvb_usb_stop_feed; + d->demux.write_to_decoder = NULL; + if ((ret = dvb_dmx_init(&d->demux)) < 0) { + err("dvb_dmx_init failed: error %d",ret); + goto err_dmx; + } + + d->dmxdev.filternum = d->demux.filternum; + d->dmxdev.demux = &d->demux.dmx; + d->dmxdev.capabilities = 0; + if ((ret = dvb_dmxdev_init(&d->dmxdev, &d->dvb_adap)) < 0) { + err("dvb_dmxdev_init failed: error %d",ret); + goto err_dmx_dev; + } + + dvb_net_init(&d->dvb_adap, &d->dvb_net, &d->demux.dmx); + + goto success; +err_dmx_dev: + dvb_dmx_release(&d->demux); +err_dmx: + dvb_unregister_adapter(&d->dvb_adap); +err: + return ret; +success: + d->state |= DVB_USB_STATE_DVB; + return 0; +} + +int dvb_usb_dvb_exit(struct dvb_usb_device *d) +{ + if (d->state & DVB_USB_STATE_DVB) { + deb_info("unregistering DVB part\n"); + dvb_net_release(&d->dvb_net); + d->demux.dmx.close(&d->demux.dmx); + dvb_dmxdev_release(&d->dmxdev); + dvb_dmx_release(&d->demux); + dvb_unregister_adapter(&d->dvb_adap); + d->state &= ~DVB_USB_STATE_DVB; + } + return 0; +} + +static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) +{ + struct dvb_usb_device *d = fe->dvb->priv; + + if (d->props.power_ctrl) + d->props.power_ctrl(d,1); + + if (d->fe_init) + d->fe_init(fe); + + return 0; +} + +static int dvb_usb_fe_sleep(struct dvb_frontend *fe) +{ + struct dvb_usb_device *d = fe->dvb->priv; + + if (d->fe_sleep) + d->fe_sleep(fe); + + if (d->props.power_ctrl) + d->props.power_ctrl(d,0); + + return 0; +} + +int dvb_usb_fe_init(struct dvb_usb_device* d) +{ + if (d->props.frontend_attach == NULL) { + err("strange '%s' don't want to attach a frontend.",d->desc->name); + return 0; + } + + d->props.frontend_attach(d); + + /* re-assign sleep and wakeup functions */ + if (d->fe != NULL) { + d->fe_init = d->fe->ops->init; d->fe->ops->init = dvb_usb_fe_wakeup; + d->fe_sleep = d->fe->ops->sleep; d->fe->ops->sleep = dvb_usb_fe_sleep; + + if (dvb_register_frontend(&d->dvb_adap, d->fe)) { + err("Frontend registration failed."); + if (d->fe->ops->release) + d->fe->ops->release(d->fe); + d->fe = NULL; + return -ENODEV; + } + } else + err("no frontend was attached by '%s'",d->desc->name); + + if (d->props.tuner_attach != NULL) + d->props.tuner_attach(d); + + return 0; +} + +int dvb_usb_fe_exit(struct dvb_usb_device *d) +{ + if (d->fe != NULL) + dvb_unregister_frontend(d->fe); + return 0; +} diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c b/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c new file mode 100644 index 000000000000..5244e39770a0 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c @@ -0,0 +1,100 @@ +/* dvb-usb-firmware.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for downloading the firmware to Cypress FX 1 and 2 based devices. + * + * FIXME: This part does actually not belong to dvb-usb, but to the usb-subsystem. + */ +#include "dvb-usb-common.h" + +#include +#include + +struct usb_cypress_controller { + int id; + const char *name; /* name of the usb controller */ + u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */ +}; + +static struct usb_cypress_controller cypress[] = { + { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 }, + { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 }, + { .id = CYPRESS_FX2, .name = "Cypress FX2", .cpu_cs_register = 0xe600 }, +}; + +/* + * load a firmware packet to the device + */ +static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev,0), + 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5*HZ); +} + +int usb_cypress_load_firmware(struct usb_device *udev, const char *filename, int type) +{ + const struct firmware *fw = NULL; + u16 addr; + u8 *b,*p; + int ret = 0,i; + + if ((ret = request_firmware(&fw, filename, &udev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems.", + filename); + return ret; + } + + info("downloading firmware from file '%s' to the '%s'",filename,cypress[type].name); + + p = kmalloc(fw->size,GFP_KERNEL); + if (p != NULL) { + u8 reset; + /* + * you cannot use the fw->data as buffer for + * usb_control_msg, a new buffer has to be + * created + */ + memcpy(p,fw->data,fw->size); + + /* stop the CPU */ + reset = 1; + if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) + err("could not stop the USB controller CPU."); + for(i = 0; p[i+3] == 0 && i < fw->size; ) { + b = (u8 *) &p[i]; + addr = cpu_to_le16( *((u16 *) &b[1]) ); + + deb_fw("writing to address 0x%04x (buffer: 0x%02x%02x)\n",addr,b[1],b[2]); + + ret = usb_cypress_writemem(udev,addr,&b[4],b[0]); + + if (ret != b[0]) { + err("error while transferring firmware " + "(transferred size: %d, block size: %d)", + ret,b[0]); + ret = -EINVAL; + break; + } + i += 5 + b[0]; + } + /* length in ret */ + if (ret > 0) + ret = 0; + /* restart the CPU */ + reset = 0; + if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + + kfree(p); + } else { + ret = -ENOMEM; + } + release_firmware(fw); + + return ret; +} diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c new file mode 100644 index 000000000000..9f0a8d90d146 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -0,0 +1,118 @@ +/* dvb-usb-i2c.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for (de-)initializing an I2C adapter. + */ +#include "dvb-usb-common.h" + +int dvb_usb_i2c_init(struct dvb_usb_device *d) +{ + int ret = 0; + + if (!(d->props.caps & DVB_USB_IS_AN_I2C_ADAPTER)) + return 0; + + if (d->props.i2c_algo == NULL) { + err("no i2c algorithm specified"); + return -EINVAL; + } + + strncpy(d->i2c_adap.name,d->desc->name,I2C_NAME_SIZE); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + d->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, +#else + d->i2c_adap.class = I2C_CLASS_TV_DIGITAL, +#endif + d->i2c_adap.algo = d->props.i2c_algo; + d->i2c_adap.algo_data = NULL; + d->i2c_adap.id = I2C_ALGO_BIT; + + i2c_set_adapdata(&d->i2c_adap, d); + + if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0) + err("could not add i2c adapter"); + + d->state |= DVB_USB_STATE_I2C; + + return ret; +} + +int dvb_usb_i2c_exit(struct dvb_usb_device *d) +{ + if (d->state & DVB_USB_STATE_I2C) + i2c_del_adapter(&d->i2c_adap); + d->state &= ~DVB_USB_STATE_I2C; + return 0; +} + +int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) +{ + struct dvb_usb_device *d = fe->dvb->priv; + struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = d->pll_init, .len = 4 }; + int ret = 0; + + /* if there is nothing to initialize */ + if (d->pll_init[0] == 0x00 && d->pll_init[1] == 0x00 && + d->pll_init[2] == 0x00 && d->pll_init[3] == 0x00) + return 0; + + if (d->tuner_pass_ctrl) + d->tuner_pass_ctrl(fe,1,d->pll_addr); + + deb_pll("pll init: %x\n",d->pll_addr); + deb_pll("pll-buf: %x %x %x %x\n",d->pll_init[0],d->pll_init[1], + d->pll_init[2],d->pll_init[3]); + + if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { + err("tuner i2c write failed for pll_init."); + ret = -EREMOTEIO; + } + msleep(1); + + if (d->tuner_pass_ctrl) + d->tuner_pass_ctrl(fe,0,d->pll_addr); + return ret; +} +EXPORT_SYMBOL(dvb_usb_pll_init_i2c); + +int dvb_usb_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 b[5]) +{ + struct dvb_usb_device *d = fe->dvb->priv; + + deb_pll("pll addr: %x, freq: %d %p\n",d->pll_addr,fep->frequency,d->pll_desc); + + b[0] = d->pll_addr << 1; + dvb_pll_configure(d->pll_desc,&b[1],fep->frequency,fep->u.ofdm.bandwidth); + + deb_pll("pll-buf: %x %x %x %x %x\n",b[0],b[1],b[2],b[3],b[4]); + + return 0; +} +EXPORT_SYMBOL(dvb_usb_pll_set); + +int dvb_usb_pll_set_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_device *d = fe->dvb->priv; + int ret = 0; + u8 b[5]; + struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = &b[1], .len = 4 }; + + dvb_usb_pll_set(fe,fep,b); + + if (d->tuner_pass_ctrl) + d->tuner_pass_ctrl(fe,1,d->pll_addr); + + if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { + err("tuner i2c write failed for pll_set."); + ret = -EREMOTEIO; + } + msleep(1); + + if (d->tuner_pass_ctrl) + d->tuner_pass_ctrl(fe,0,d->pll_addr); + + return ret; +} +EXPORT_SYMBOL(dvb_usb_pll_set_i2c); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h new file mode 100644 index 000000000000..bcb34191868b --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -0,0 +1,83 @@ +/* dvb-usb-ids.h is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see + * dvb-usb-init.c for copyright information. + * + * a header file containing define's for the USB device supported by the + * various drivers. + */ +#ifndef _DVB_USB_IDS_H_ +#define _DVB_USB_IDS_H_ + +/* Vendor IDs */ +#define USB_VID_ADSTECH 0x06e1 +#define USB_VID_ANCHOR 0x0547 +#define USB_VID_AVERMEDIA_UNK 0x14aa +#define USB_VID_AVERMEDIA 0x07ca +#define USB_VID_COMPRO 0x185b +#define USB_VID_COMPRO_UNK 0x145f +#define USB_VID_CYPRESS 0x04b4 +#define USB_VID_DIBCOM 0x10b8 +#define USB_VID_DVICO 0x0fe9 +#define USB_VID_EMPIA 0xeb1a +#define USB_VID_GRANDTEC 0x5032 +#define USB_VID_HANFTEK 0x15f4 +#define USB_VID_HAUPPAUGE 0x2040 +#define USB_VID_HYPER_PALTEK 0x1025 +#define USB_VID_VISIONPLUS 0x13d3 +#define USB_VID_TWINHAN 0x1822 +#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 + +/* Product IDs */ +#define USB_PID_ADSTECH_USB2_COLD 0xa333 +#define USB_PID_ADSTECH_USB2_WARM 0xa334 +#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 +#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 +#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 +#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801 +#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 +#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 +#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c +#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 +#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 +#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 +#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 +#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 +#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_KWORLD_VSTREAM_COLD 0x17de +#define USB_PID_KWORLD_VSTREAM_WARM 0x17df +#define USB_PID_TWINHAN_VP7041_COLD 0x3201 +#define USB_PID_TWINHAN_VP7041_WARM 0x3202 +#define USB_PID_TWINHAN_VP7045_COLD 0x3205 +#define USB_PID_TWINHAN_VP7045_WARM 0x3206 +#define USB_PID_DNTV_TINYUSB2_COLD 0x3223 +#define USB_PID_DNTV_TINYUSB2_WARM 0x3224 +#define USB_PID_TWINHAN_VP7021_COLD 0x3207 +#define USB_PID_TWINHAN_VP7021_WARM 0x3208 +#define USB_PID_ULTIMA_TVBOX_COLD 0x8105 +#define USB_PID_ULTIMA_TVBOX_WARM 0x8106 +#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 +#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 +#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 +#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 +#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 +#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e +#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f +#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 +#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 +#define USB_PID_DTT200U_COLD 0x0201 +#define USB_PID_DTT200U_WARM 0x0301 +#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 +#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 +#define USB_PID_NEBULA_DIGITV 0x0201 +#define USB_PID_DVICO_BLUEBIRD_LGZ201 0xdb00 +#define USB_PID_DVICO_BLUEBIRD_TH7579 0xdb10 +#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 +#define USB_PID_DVICO_BLUEBIRD_LGZ201_1 0xdb01 +#define USB_PID_DVICO_BLUEBIRD_TH7579_2 0xdb11 + + +#endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c new file mode 100644 index 000000000000..3aadec974cf1 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c @@ -0,0 +1,211 @@ +/* + * DVB USB library - provides a generic interface for a DVB USB device driver. + * + * dvb-usb-init.c + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dvb-usb-common.h" + +/* debug */ +int dvb_usb_debug; +module_param_named(debug,dvb_usb_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." DVB_USB_DEBUG_STATUS); + +/* general initialization functions */ +int dvb_usb_exit(struct dvb_usb_device *d) +{ + deb_info("state before exiting everything: %x\n",d->state); + dvb_usb_remote_exit(d); + dvb_usb_fe_exit(d); + dvb_usb_i2c_exit(d); + dvb_usb_dvb_exit(d); + dvb_usb_urb_exit(d); + deb_info("state should be zero now: %x\n",d->state); + d->state = DVB_USB_STATE_INIT; + kfree(d->priv); + kfree(d); + return 0; +} + +static int dvb_usb_init(struct dvb_usb_device *d) +{ + int ret = 0; + + sema_init(&d->usb_sem, 1); + sema_init(&d->i2c_sem, 1); + + d->state = DVB_USB_STATE_INIT; + +/* check the capabilites and set appropriate variables */ + +/* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && !(d->props.caps & DVB_USB_HAS_PID_FILTER)) { + err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a HW PID filter)"); + return -ENODEV; + } + + if ((d->udev->speed == USB_SPEED_FULL && d->props.caps & DVB_USB_HAS_PID_FILTER) || + (d->props.caps & DVB_USB_NEED_PID_FILTERING)) { + info("will use the device's hw PID filter."); + d->pid_filtering = 1; + d->max_feed_count = d->props.pid_filter_count; + } else { + info("will pass the complete MPEG2 transport stream to the demuxer."); + d->pid_filtering = 0; + d->max_feed_count = 255; + } + + if (d->props.power_ctrl) + d->props.power_ctrl(d,1); + + if ((ret = dvb_usb_urb_init(d)) || + (ret = dvb_usb_dvb_init(d)) || + (ret = dvb_usb_i2c_init(d)) || + (ret = dvb_usb_fe_init(d))) { + dvb_usb_exit(d); + return ret; + } + + if ((ret = dvb_usb_remote_init(d))) + err("could not initialize remote control."); + + if (d->props.power_ctrl) + d->props.power_ctrl(d,0); + + return 0; +} + +/* determine the name and the state of the just found USB device */ +static struct dvb_usb_device_description * dvb_usb_find_device(struct usb_device *udev,struct dvb_usb_properties *props, int *cold) +{ + int i,j; + struct dvb_usb_device_description *desc = NULL; + *cold = -1; + + for (i = 0; i < props->num_device_descs; i++) { + + for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].cold_ids[j] != NULL; j++) { + deb_info("check for cold %x %x\n",props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct); + if (props->devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + props->devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 1; + desc = &props->devices[i]; + break; + } + } + + if (desc != NULL) + break; + + for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].warm_ids[j] != NULL; j++) { + deb_info("check for warm %x %x\n",props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct); + if (props->devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + props->devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 0; + desc = &props->devices[i]; + break; + } + } + } + + if (desc != NULL && props->identify_state != NULL) + props->identify_state(udev,props,&desc,cold); + + return desc; +} + +/* + * USB + */ +int dvb_usb_device_init(struct usb_interface *intf, struct dvb_usb_properties *props, struct module *owner) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct dvb_usb_device *d = NULL; + struct dvb_usb_device_description *desc = NULL; + + int ret = -ENOMEM,cold=0; + + if ((desc = dvb_usb_find_device(udev,props,&cold)) == NULL) { + deb_err("something went very wrong, device was not found in current device list - let's see what comes next.\n"); + return -ENODEV; + } + + if (cold) { + info("found a '%s' in cold state, will try to load a firmware",desc->name); + ret = usb_cypress_load_firmware(udev,props->firmware,props->usb_ctrl); + } else { + info("found a '%s' in warm state.",desc->name); + d = kmalloc(sizeof(struct dvb_usb_device),GFP_KERNEL); + if (d == NULL) { + err("no memory for 'struct dvb_usb_device'"); + return ret; + } + memset(d,0,sizeof(struct dvb_usb_device)); + + d->udev = udev; + memcpy(&d->props,props,sizeof(struct dvb_usb_properties)); + d->desc = desc; + d->owner = owner; + + if (d->props.size_of_priv > 0) { + d->priv = kmalloc(d->props.size_of_priv,GFP_KERNEL); + if (d->priv == NULL) { + err("no memory for priv in 'struct dvb_usb_device'"); + kfree(d); + return -ENOMEM; + } + memset(d->priv,0,d->props.size_of_priv); + } + + usb_set_intfdata(intf, d); + + ret = dvb_usb_init(d); + } + + if (ret == 0) + info("%s successfully initialized and connected.",desc->name); + else + info("%s error while loading driver (%d)",desc->name,ret); + return ret; +} +EXPORT_SYMBOL(dvb_usb_device_init); + +void dvb_usb_device_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + const char *name = "generic DVB-USB module"; + + usb_set_intfdata(intf,NULL); + if (d != NULL && d->desc != NULL) { + name = d->desc->name; + dvb_usb_exit(d); + } + info("%s successfully deinitialized and disconnected.",name); + +} +EXPORT_SYMBOL(dvb_usb_device_exit); + +/* module stuff */ +static int __init dvb_usb_module_init(void) +{ + return 0; +} + +static void __exit dvb_usb_module_exit(void) +{ +} + +module_init (dvb_usb_module_init); +module_exit (dvb_usb_module_exit); + +MODULE_VERSION("0.3"); +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c new file mode 100644 index 000000000000..9f1e23f82bae --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -0,0 +1,175 @@ +/* dvb-usb-remote.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing the the input-device and for handling remote-control-queries. + */ +#include "dvb-usb-common.h" + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + * whether the remote control has received anything. + * + * TODO: Fix the repeat rate of the input device. + */ +static void dvb_usb_read_remote_control(void *data) +{ + struct dvb_usb_device *d = data; + u32 event; + int state; + + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + + if (d->props.rc_query(d,&event,&state)) { + err("error while querying for an remote control event."); + goto schedule; + } + + + switch (state) { + case REMOTE_NO_KEY_PRESSED: + break; + case REMOTE_KEY_PRESSED: + deb_rc("key pressed\n"); + d->last_event = event; + case REMOTE_KEY_REPEAT: + deb_rc("key repeated\n"); + input_event(&d->rc_input_dev, EV_KEY, event, 1); + input_event(&d->rc_input_dev, EV_KEY, d->last_event, 0); + input_sync(&d->rc_input_dev); + break; + default: + break; + } + +/* improved repeat handling ??? + switch (state) { + case REMOTE_NO_KEY_PRESSED: + deb_rc("NO KEY PRESSED\n"); + if (d->last_state != REMOTE_NO_KEY_PRESSED) { + deb_rc("releasing event %d\n",d->last_event); + input_event(&d->rc_input_dev, EV_KEY, d->last_event, 0); + input_sync(&d->rc_input_dev); + } + d->last_state = REMOTE_NO_KEY_PRESSED; + d->last_event = 0; + break; + case REMOTE_KEY_PRESSED: + deb_rc("KEY PRESSED\n"); + deb_rc("pressing event %d\n",event); + + input_event(&d->rc_input_dev, EV_KEY, event, 1); + input_sync(&d->rc_input_dev); + + d->last_event = event; + d->last_state = REMOTE_KEY_PRESSED; + break; + case REMOTE_KEY_REPEAT: + deb_rc("KEY_REPEAT\n"); + if (d->last_state != REMOTE_NO_KEY_PRESSED) { + deb_rc("repeating event %d\n",d->last_event); + input_event(&d->rc_input_dev, EV_KEY, d->last_event, 2); + input_sync(&d->rc_input_dev); + d->last_state = REMOTE_KEY_REPEAT; + } + default: + break; + } +*/ + +schedule: + schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc_interval)); +} + +int dvb_usb_remote_init(struct dvb_usb_device *d) +{ + int i; + if (d->props.rc_key_map == NULL) + return 0; + + /* Initialise the remote-control structures.*/ + init_input_dev(&d->rc_input_dev); + + d->rc_input_dev.evbit[0] = BIT(EV_KEY); + d->rc_input_dev.keycodesize = sizeof(unsigned char); + d->rc_input_dev.keycodemax = KEY_MAX; + d->rc_input_dev.name = "IR-receiver inside an USB DVB receiver"; + + /* set the bits for the keys */ + deb_rc("key map size: %d\n",d->props.rc_key_map_size); + for (i = 0; i < d->props.rc_key_map_size; i++) { + deb_rc("setting bit for event %d item %d\n",d->props.rc_key_map[i].event, i); + set_bit(d->props.rc_key_map[i].event, d->rc_input_dev.keybit); + } + + /* Start the remote-control polling. */ + if (d->props.rc_interval < 40) + d->props.rc_interval = 100; /* default */ + + /* setting these two values to non-zero, we have to manage key repeats */ + d->rc_input_dev.rep[REP_PERIOD] = d->props.rc_interval; + d->rc_input_dev.rep[REP_DELAY] = d->props.rc_interval + 150; + + input_register_device(&d->rc_input_dev); + + INIT_WORK(&d->rc_query_work, dvb_usb_read_remote_control, d); + + info("schedule remote query interval to %d msecs.",d->props.rc_interval); + schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc_interval)); + + d->state |= DVB_USB_STATE_REMOTE; + + return 0; +} + +int dvb_usb_remote_exit(struct dvb_usb_device *d) +{ + if (d->state & DVB_USB_STATE_REMOTE) { + cancel_delayed_work(&d->rc_query_work); + flush_scheduled_work(); + input_unregister_device(&d->rc_input_dev); + } + d->state &= ~DVB_USB_STATE_REMOTE; + return 0; +} + +#define DVB_USB_RC_NEC_EMPTY 0x00 +#define DVB_USB_RC_NEC_KEY_PRESSED 0x01 +#define DVB_USB_RC_NEC_KEY_REPEATED 0x02 +int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, + u8 keybuf[5], u32 *event, int *state) +{ + int i; + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + switch (keybuf[0]) { + case DVB_USB_RC_NEC_EMPTY: + break; + case DVB_USB_RC_NEC_KEY_PRESSED: + if ((u8) ~keybuf[1] != keybuf[2] || + (u8) ~keybuf[3] != keybuf[4]) { + deb_err("remote control checksum failed.\n"); + break; + } + /* See if we can match the raw key code. */ + for (i = 0; i < sizeof(keymap)/sizeof(struct dvb_usb_rc_key); i++) + if (keymap[i].custom == keybuf[1] && + keymap[i].data == keybuf[3]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + break; + } + deb_err("key mapping failed - no appropriate key found in keymapping\n"); + break; + case DVB_USB_RC_NEC_KEY_REPEATED: + *state = REMOTE_KEY_REPEAT; + break; + default: + deb_err("unkown type of remote status: %d\n",keybuf[0]); + break; + } + return 0; +} +EXPORT_SYMBOL(dvb_usb_nec_rc_key_to_event); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c new file mode 100644 index 000000000000..83d476fb410a --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -0,0 +1,211 @@ +/* dvb-usb-urb.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * This file contains functions for initializing and handling the + * USB and URB stuff. + */ +#include "dvb-usb-common.h" + +int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, + u16 rlen, int delay_ms) +{ + int actlen,ret = -ENOMEM; + + if (d->props.generic_bulk_ctrl_endpoint == 0) { + err("endpoint for generic control not specified."); + return -EINVAL; + } + + if (wbuf == NULL || wlen == 0) + return -EINVAL; + + if ((ret = down_interruptible(&d->usb_sem))) + return ret; + + debug_dump(wbuf,wlen,deb_xfer); + + ret = usb_bulk_msg(d->udev,usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint), wbuf,wlen,&actlen, + 2*HZ); + + if (ret) + err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); + else + ret = actlen != wlen ? -1 : 0; + + /* an answer is expected, and no error before */ + if (!ret && rbuf && rlen) { + if (delay_ms) + msleep(delay_ms); + + ret = usb_bulk_msg(d->udev,usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen, + 2*HZ); + + if (ret) + err("recv bulk message failed: %d",ret); + else + debug_dump(rbuf,actlen,deb_xfer); + } + + up(&d->usb_sem); + return ret; +} +EXPORT_SYMBOL(dvb_usb_generic_rw); + +int dvb_usb_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len) +{ + return dvb_usb_generic_rw(d,buf,len,NULL,0,0); +} +EXPORT_SYMBOL(dvb_usb_generic_write); + +static void dvb_usb_bulk_urb_complete(struct urb *urb, struct pt_regs *ptregs) +{ + struct dvb_usb_device *d = urb->context; + + deb_ts("bulk urb completed. feedcount: %d, status: %d, length: %d\n",d->feedcount,urb->status, + urb->actual_length); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + deb_ts("urb completition error %d.", urb->status); + break; + } + + if (d->feedcount > 0 && urb->actual_length > 0) { + if (d->state & DVB_USB_STATE_DVB) + dvb_dmx_swfilter(&d->demux, (u8*) urb->transfer_buffer,urb->actual_length); + } else + deb_ts("URB dropped because of feedcount.\n"); + + usb_submit_urb(urb,GFP_ATOMIC); +} + +int dvb_usb_urb_kill(struct dvb_usb_device *d) +{ + int i; + for (i = 0; i < d->urbs_submitted; i++) { + deb_info("killing URB no. %d.\n",i); + + /* stop the URB */ + usb_kill_urb(d->urb_list[i]); + } + d->urbs_submitted = 0; + return 0; +} + +int dvb_usb_urb_submit(struct dvb_usb_device *d) +{ + int i,ret; + for (i = 0; i < d->urbs_initialized; i++) { + deb_info("submitting URB no. %d\n",i); + if ((ret = usb_submit_urb(d->urb_list[i],GFP_ATOMIC))) { + err("could not submit URB no. %d - get them all back\n",i); + dvb_usb_urb_kill(d); + return ret; + } + d->urbs_submitted++; + } + return 0; +} + +static int dvb_usb_bulk_urb_init(struct dvb_usb_device *d) +{ + int i,bufsize = d->props.urb.count * d->props.urb.u.bulk.buffersize; + + deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize); + /* allocate the actual buffer for the URBs */ + if ((d->buffer = usb_buffer_alloc(d->udev, bufsize, SLAB_ATOMIC, &d->dma_handle)) == NULL) { + deb_info("not enough memory for urb-buffer allocation.\n"); + return -ENOMEM; + } + deb_info("allocation successful\n"); + memset(d->buffer,0,bufsize); + + d->state |= DVB_USB_STATE_URB_BUF; + + /* allocate the URBs */ + for (i = 0; i < d->props.urb.count; i++) { + if (!(d->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) { + return -ENOMEM; + } + + usb_fill_bulk_urb( d->urb_list[i], d->udev, + usb_rcvbulkpipe(d->udev,d->props.urb.endpoint), + &d->buffer[i*d->props.urb.u.bulk.buffersize], + d->props.urb.u.bulk.buffersize, + dvb_usb_bulk_urb_complete, d); + + d->urb_list[i]->transfer_flags = 0; + d->urbs_initialized++; + } + return 0; +} + +int dvb_usb_urb_init(struct dvb_usb_device *d) +{ + /* + * when reloading the driver w/o replugging the device + * sometimes a timeout occures, this helps + */ + if (d->props.generic_bulk_ctrl_endpoint != 0) { + usb_clear_halt(d->udev,usb_sndbulkpipe(d->udev,d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev,usb_rcvbulkpipe(d->udev,d->props.generic_bulk_ctrl_endpoint)); + } + usb_clear_halt(d->udev,usb_rcvbulkpipe(d->udev,d->props.urb.endpoint)); + + /* allocate the array for the data transfer URBs */ + d->urb_list = kmalloc(d->props.urb.count * sizeof(struct urb *),GFP_KERNEL); + if (d->urb_list == NULL) + return -ENOMEM; + memset(d->urb_list,0,d->props.urb.count * sizeof(struct urb *)); + d->state |= DVB_USB_STATE_URB_LIST; + + switch (d->props.urb.type) { + case DVB_USB_BULK: + return dvb_usb_bulk_urb_init(d); + case DVB_USB_ISOC: + err("isochronous transfer not yet implemented in dvb-usb."); + return -EINVAL; + default: + err("unkown URB-type for data transfer."); + return -EINVAL; + } +} + +int dvb_usb_urb_exit(struct dvb_usb_device *d) +{ + int i; + + dvb_usb_urb_kill(d); + + if (d->state & DVB_USB_STATE_URB_LIST) { + for (i = 0; i < d->urbs_initialized; i++) { + if (d->urb_list[i] != NULL) { + deb_info("freeing URB no. %d.\n",i); + /* free the URBs */ + usb_free_urb(d->urb_list[i]); + } + } + d->urbs_initialized = 0; + /* free the urb array */ + kfree(d->urb_list); + d->state &= ~DVB_USB_STATE_URB_LIST; + } + + if (d->state & DVB_USB_STATE_URB_BUF) + usb_buffer_free(d->udev, d->props.urb.u.bulk.buffersize * d->props.urb.count, + d->buffer, d->dma_handle); + + d->state &= ~DVB_USB_STATE_URB_BUF; + return 0; +} diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h new file mode 100644 index 000000000000..abcee1943f64 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -0,0 +1,315 @@ +/* dvb-usb.h is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * see dvb-usb-init.c for copyright information. + * + * the headerfile, all dvb-usb-drivers have to include. + */ +#ifndef __DVB_USB_H__ +#define __DVB_USB_H__ + +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" + +#include "dvb-pll.h" + +#include "dvb-usb-ids.h" + +/* debug */ +#ifdef CONFIG_DVB_USB_DEBUG +#define dprintk(var,level,args...) \ + do { if ((var & level)) { printk(args); } } while (0) + +#define debug_dump(b,l,func) {\ + int loop_; \ + for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \ + func("\n");\ +} +#define DVB_USB_DEBUG_STATUS +#else +#define dprintk(args...) +#define debug_dump(b,l,func) + +#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" + +#endif + +/* generic log methods - taken from usb.h */ +#ifndef DVB_USB_LOG_PREFIX + #define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)" +#endif + +#undef err +#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) + +/** + * struct dvb_usb_device_description - name and its according USB IDs + * @name: real name of the box, regardless which DVB USB device class is in use + * @cold_ids: array of struct usb_device_id which describe the device in + * pre-firmware state + * @warm_ids: array of struct usb_device_id which describe the device in + * post-firmware state + * + * Each DVB USB device class can have one or more actual devices, this struct + * assigns a name to it. + */ +struct dvb_usb_device_description { + const char *name; + +#define DVB_USB_ID_MAX_NUM 15 + struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM]; + struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM]; +}; + +/** + * struct dvb_usb_rc_key - a remote control key and its input-event + * @custom: the vendor/custom part of the key + * @data: the actual key part + * @event: the input event assigned to key identified by custom and data + */ +struct dvb_usb_rc_key { + u8 custom,data; + u32 event; +}; + +struct dvb_usb_device; + +/** + * struct dvb_usb_properties - properties of a dvb-usb-device + * @caps: capabilites of the DVB USB device. + * @pid_filter_count: number of PID filter position in the optional hardware + * PID-filter. + * + * @usb_ctrl: which USB device-side controller is in use. Needed for firmware + * download. + * @firmware: name of the firmware file. + * + * @size_of_priv: how many bytes shall be allocated for the private field + * of struct dvb_usb_device. + * + * @power_ctrl: called to enable/disable power of the device. + * @streaming_crtl: called to start and stop the MPEG2-TS streaming of the + * device (not URB submitting/killing). + * @pid_filter_ctrl: called to en/disable the PID filter, if any. + * @pid_filter: called to set/unset a PID for filtering. + * + * @read_mac_address: called to read the MAC address of the device. + * + * @frontend_attach: called to attach the possible frontends (fill fe-field + * of struct dvb_usb_device). + * @tuner_attach: called to attach the correct tuner and to fill pll_addr, + * pll_desc and pll_init_buf of struct dvb_usb_device). + * @identify_state: called to determine the state (cold or warm), when it + * is not distinguishable by the USB IDs. + * + * @rc_key_map: a hard-wired array of struct dvb_usb_rc_key (NULL to disable + * remote control handling). + * @rc_key_map_size: number of items in @rc_key_map. + * @rc_query: called to query an event event. + * @rc_interval: time in ms between two queries. + * + * @i2c_algo: i2c_algorithm if the device has I2CoverUSB. + * + * @generic_bulk_ctrl_endpoint: most of the DVB USB devices have a generic + * endpoint which received control messages with bulk transfers. When this + * is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write- + * helper functions. + * + * @urb: describes the kind of USB transfer used for MPEG2-TS-streaming. + * Currently only BULK is implemented + * + * @num_device_descs: number of struct dvb_usb_device_description in @devices + * @devices: array of struct dvb_usb_device_description compatibles with these + * properties. + */ +struct dvb_usb_properties { + +#define DVB_USB_HAS_PID_FILTER 0x01 +#define DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF 0x02 +#define DVB_USB_NEED_PID_FILTERING 0x04 +#define DVB_USB_IS_AN_I2C_ADAPTER 0x08 + int caps; + int pid_filter_count; + +#define CYPRESS_AN2135 0 +#define CYPRESS_AN2235 1 +#define CYPRESS_FX2 2 + int usb_ctrl; + const char *firmware; + + int size_of_priv; + + int (*power_ctrl) (struct dvb_usb_device *, int); + int (*streaming_ctrl) (struct dvb_usb_device *, int); + int (*pid_filter_ctrl) (struct dvb_usb_device *, int); + int (*pid_filter) (struct dvb_usb_device *, int, u16, int); + + int (*read_mac_address) (struct dvb_usb_device *, u8 []); + int (*frontend_attach) (struct dvb_usb_device *); + int (*tuner_attach) (struct dvb_usb_device *); + + int (*identify_state) (struct usb_device *, struct dvb_usb_properties *, + struct dvb_usb_device_description **, int *); + +/* remote control properties */ +#define REMOTE_NO_KEY_PRESSED 0x00 +#define REMOTE_KEY_PRESSED 0x01 +#define REMOTE_KEY_REPEAT 0x02 + struct dvb_usb_rc_key *rc_key_map; + int rc_key_map_size; + int (*rc_query) (struct dvb_usb_device *, u32 *, int *); + int rc_interval; + + struct i2c_algorithm *i2c_algo; + + int generic_bulk_ctrl_endpoint; + + struct { +#define DVB_USB_BULK 1 +#define DVB_USB_ISOC 2 + int type; + int count; + int endpoint; + + union { + struct { + int buffersize; /* per URB */ + } bulk; + struct { + int framesperurb; + int framesize; + } isoc; + } u; + } urb; + + int num_device_descs; + struct dvb_usb_device_description devices[8]; +}; + + +/** + * struct dvb_usb_device - object of a DVB USB device + * @props: copy of the struct dvb_usb_properties this device belongs to. + * @desc: pointer to the device's struct dvb_usb_device_description. + * @state: initialization and runtime state of the device. + * + * @udev: pointer to the device's struct usb_device. + * @urb_list: array of dynamically allocated struct urb for the MPEG2-TS- + * streaming. + * @buffer: buffer used to streaming. + * @dma_handle: dma_addr_t for buffer. + * @urbs_initialized: number of URBs initialized. + * @urbs_submitted: number of URBs submitted. + * @feedcount: number of reqested feeds (used for streaming-activation) + * @pid_filtering: is hardware pid_filtering used or not. + * @usb_sem: semaphore of USB control messages (reading needs two messages) + * @i2c_sem: semaphore for i2c-transfers + * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB + * @pll_addr: I2C address of the tuner for programming + * @pll_init: array containing the initialization buffer + * @pll_desc: pointer to the appropriate struct dvb_pll_desc + * @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod + * @dvb_adap: device's dvb_adapter. + * @dmxdev: device's dmxdev. + * @demux: device's software demuxer. + * @dvb_net: device's dvb_net interfaces. + * @dvb_frontend: device's frontend. + * @max_feed_count: how many feeds can be handled simultaneously by this + * device + * @fe_sleep: rerouted frontend-sleep function. + * @fe_init: rerouted frontend-init (wakeup) function. + * @rc_input_dev: input device for the remote control. + * @rc_query_work: struct work_struct frequent rc queries + * @last_event: last triggered event + * @last_state: last state (no, pressed, repeat) + * @owner: owner of the dvb_adapter + * @priv: private data of the actual driver (allocate by dvb-usb, size defined + * in size_of_priv of dvb_usb_properties). + */ +struct dvb_usb_device { + struct dvb_usb_properties props; + struct dvb_usb_device_description *desc; + +#define DVB_USB_STATE_INIT 0x000 +#define DVB_USB_STATE_URB_LIST 0x001 +#define DVB_USB_STATE_URB_BUF 0x002 +#define DVB_USB_STATE_DVB 0x004 +#define DVB_USB_STATE_I2C 0x008 +#define DVB_USB_STATE_REMOTE 0x010 +#define DVB_USB_STATE_URB_SUBMIT 0x020 + int state; + + /* usb */ + struct usb_device *udev; + struct urb **urb_list; + u8 *buffer; + dma_addr_t dma_handle; + int urbs_initialized; + int urbs_submitted; + + int feedcount; + int pid_filtering; + + /* locking */ + struct semaphore usb_sem; + + /* i2c */ + struct semaphore i2c_sem; + struct i2c_adapter i2c_adap; + + /* tuner programming information */ + u8 pll_addr; + u8 pll_init[4]; + struct dvb_pll_desc *pll_desc; + int (*tuner_pass_ctrl)(struct dvb_frontend *, int, u8); + + /* dvb */ + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend* fe; + int max_feed_count; + + int (*fe_sleep) (struct dvb_frontend *); + int (*fe_init) (struct dvb_frontend *); + + /* remote control */ + struct input_dev rc_input_dev; + struct work_struct rc_query_work; + u32 last_event; + int last_state; + + struct module *owner; + + void *priv; +}; + +extern int dvb_usb_device_init(struct usb_interface *, struct dvb_usb_properties *, struct module *); +extern void dvb_usb_device_exit(struct usb_interface *); + +/* the generic read/write method for device control */ +extern int dvb_usb_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16,int); +extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16); + +/* commonly used remote control parsing */ +extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *); + +/* commonly used pll init and set functions */ +extern int dvb_usb_pll_init_i2c(struct dvb_frontend *); +extern int dvb_usb_pll_set(struct dvb_frontend *, struct dvb_frontend_parameters *, u8[]); +extern int dvb_usb_pll_set_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); + + +#endif diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c new file mode 100644 index 000000000000..9d83781aef95 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -0,0 +1,236 @@ +/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2 + * DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS); + +#define deb_rc(args...) dprintk(debug,0x01,args) +#define deb_ee(args...) dprintk(debug,0x02,args) + +/* Hauppauge NOVA-T USB2 keys */ +static struct dvb_usb_rc_key haupp_rc_keys [] = { + { 0x1e, 0x00, KEY_0 }, + { 0x1e, 0x01, KEY_1 }, + { 0x1e, 0x02, KEY_2 }, + { 0x1e, 0x03, KEY_3 }, + { 0x1e, 0x04, KEY_4 }, + { 0x1e, 0x05, KEY_5 }, + { 0x1e, 0x06, KEY_6 }, + { 0x1e, 0x07, KEY_7 }, + { 0x1e, 0x08, KEY_8 }, + { 0x1e, 0x09, KEY_9 }, + { 0x1e, 0x0a, KEY_KPASTERISK }, + { 0x1e, 0x0b, KEY_RED }, + { 0x1e, 0x0c, KEY_RADIO }, + { 0x1e, 0x0d, KEY_MENU }, + { 0x1e, 0x0e, KEY_GRAVE }, /* # */ + { 0x1e, 0x0f, KEY_MUTE }, + { 0x1e, 0x10, KEY_VOLUMEUP }, + { 0x1e, 0x11, KEY_VOLUMEDOWN }, + { 0x1e, 0x12, KEY_CHANNEL }, + { 0x1e, 0x14, KEY_UP }, + { 0x1e, 0x15, KEY_DOWN }, + { 0x1e, 0x16, KEY_LEFT }, + { 0x1e, 0x17, KEY_RIGHT }, + { 0x1e, 0x18, KEY_VIDEO }, + { 0x1e, 0x19, KEY_AUDIO }, + { 0x1e, 0x1a, KEY_MEDIA }, + { 0x1e, 0x1b, KEY_EPG }, + { 0x1e, 0x1c, KEY_TV }, + { 0x1e, 0x1e, KEY_NEXT }, + { 0x1e, 0x1f, KEY_BACK }, + { 0x1e, 0x20, KEY_CHANNELUP }, + { 0x1e, 0x21, KEY_CHANNELDOWN }, + { 0x1e, 0x24, KEY_LAST }, /* Skip backwards */ + { 0x1e, 0x25, KEY_OK }, + { 0x1e, 0x29, KEY_BLUE}, + { 0x1e, 0x2e, KEY_GREEN }, + { 0x1e, 0x30, KEY_PAUSE }, + { 0x1e, 0x32, KEY_REWIND }, + { 0x1e, 0x34, KEY_FASTFORWARD }, + { 0x1e, 0x35, KEY_PLAY }, + { 0x1e, 0x36, KEY_STOP }, + { 0x1e, 0x37, KEY_RECORD }, + { 0x1e, 0x38, KEY_YELLOW }, + { 0x1e, 0x3b, KEY_GOTO }, + { 0x1e, 0x3d, KEY_POWER }, +}; + +/* Firmware bug? sometimes, when a new key is pressed, the previous pressed key + * is delivered. No workaround yet, maybe a new firmware. + */ +static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5],cmd[2] = { DIBUSB_REQ_POLL_REMOTE, 0x35 }, data,toggle,custom; + u16 raw; + int i; + struct dibusb_state *st = d->priv; + + dvb_usb_generic_rw(d,cmd,2,key,5,0); + + *state = REMOTE_NO_KEY_PRESSED; + switch (key[0]) { + case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: + raw = ((key[1] << 8) | key[2]) >> 3; + toggle = !!(raw & 0x800); + data = raw & 0x3f; + custom = (raw >> 6) & 0x1f; + + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle); + + for (i = 0; i < ARRAY_SIZE(haupp_rc_keys); i++) { + deb_rc("c: %x, d: %x\n",haupp_rc_keys[i].data,haupp_rc_keys[i].custom); + if (haupp_rc_keys[i].data == data && + haupp_rc_keys[i].custom == custom) { + *event = haupp_rc_keys[i].event; + *state = REMOTE_KEY_PRESSED; + if (st->old_toggle == toggle) { + if (st->last_repeat_count++ < 2) + *state = REMOTE_NO_KEY_PRESSED; + } else { + st->last_repeat_count = 0; + st->old_toggle = toggle; + } + break; + } + } + + break; + case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: + default: + break; + } + + return 0; +} + +static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) +{ + int i; + u8 b; + + mac[0] = 0x00; + mac[1] = 0x0d; + mac[2] = 0xfe; + + /* this is a complete guess, but works for my box */ + for (i = 136; i < 139; i++) { + dibusb_read_eeprom_byte(d,i, &b); + + mac[5 - (i - 136)] = b; + +/* deb_ee("%02x ",b); + if ((i+1) % 16 == 0) + deb_ee("\n");*/ + } + + return 0; +} + +/* USB Driver stuff */ +static struct dvb_usb_properties nova_t_properties; + +static int nova_t_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&nova_t_properties,THIS_MODULE); +} + +/* do not change the order of the ID table */ +static struct usb_device_id nova_t_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, nova_t_table); + +static struct dvb_usb_properties nova_t_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_IS_AN_I2C_ADAPTER, + .pid_filter_count = 32, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-nova-t-usb2-01.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .power_ctrl = dibusb2_0_power_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + .read_mac_address = nova_t_read_mac_address, + + .rc_interval = 100, + .rc_key_map = haupp_rc_keys, + .rc_key_map_size = ARRAY_SIZE(haupp_rc_keys), + .rc_query = nova_t_rc_query, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { "Hauppauge WinTV-NOVA-T usb2", + { &nova_t_table[0], NULL }, + { &nova_t_table[1], NULL }, + }, + } +}; + +static struct usb_driver nova_t_driver = { + .owner = THIS_MODULE, + .name = "Hauppauge WinTV-NOVA-T usb2", + .probe = nova_t_probe, + .disconnect = dvb_usb_device_exit, + .id_table = nova_t_table, +}; + +/* module stuff */ +static int __init nova_t_module_init(void) +{ + int result; + if ((result = usb_register(&nova_t_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit nova_t_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&nova_t_driver); +} + +module_init (nova_t_module_init); +module_exit (nova_t_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c new file mode 100644 index 000000000000..aa560422ce7c --- /dev/null +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -0,0 +1,162 @@ +/* DVB USB framework compliant Linux driver for the HanfTek UMT-010 USB2.0 + * DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" + +#include "mt352.h" + +static int umt_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d }; + static u8 mt352_reset[] = { 0x50, 0x80 }; + static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 }; + static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; + static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 }; + + static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff }; + static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff }; + static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 }; + static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 }; + static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f }; + + static u8 mt352_acq_ctl[] = { 0x53, 0x50 }; + static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 }; + + mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); + udelay(2000); + mt352_write(fe, mt352_reset, sizeof(mt352_reset)); + mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio)); + + mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); + mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg)); + + mt352_write(fe, mt352_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1)); + mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2)); + mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3)); + mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4)); + mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5)); + + mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl)); + mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1)); + + return 0; +} + +static int umt_mt352_frontend_attach(struct dvb_usb_device *d) +{ + struct mt352_config umt_config; + + memset(&umt_config,0,sizeof(struct mt352_config)); + umt_config.demod_init = umt_mt352_demod_init; + umt_config.demod_address = 0xf; + umt_config.pll_set = dvb_usb_pll_set; + + d->fe = mt352_attach(&umt_config, &d->i2c_adap); + + return 0; +} + +static int umt_tuner_attach (struct dvb_usb_device *d) +{ + d->pll_addr = 0x61; + d->pll_desc = &dvb_pll_tua6034; + return 0; +} + +/* USB Driver stuff */ +static struct dvb_usb_properties umt_properties; + +static int umt_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + if (dvb_usb_device_init(intf,&umt_properties,THIS_MODULE) == 0) + return 0; + return -EINVAL; +} + +/* do not change the order of the ID table */ +static struct usb_device_id umt_table [] = { +/* 00 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, umt_table); + +static struct dvb_usb_properties umt_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-umt-010-02.fw", + + .size_of_priv = sizeof(struct dibusb_state), + + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .power_ctrl = dibusb_power_ctrl, + .frontend_attach = umt_mt352_frontend_attach, + .tuner_attach = umt_tuner_attach, + + .i2c_algo = &dibusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 20, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + + .num_device_descs = 1, + .devices = { + { "Hanftek UMT-010 DVB-T USB2.0", + { &umt_table[0], NULL }, + { &umt_table[1], NULL }, + }, + } +}; + +static struct usb_driver umt_driver = { + .owner = THIS_MODULE, + .name = "HanfTek UMT-010 USB2.0 DVB-T devices", + .probe = umt_probe, + .disconnect = dvb_usb_device_exit, + .id_table = umt_table, +}; + +/* module stuff */ +static int __init umt_module_init(void) +{ + int result; + if ((result = usb_register(&umt_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit umt_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&umt_driver); +} + +module_init (umt_module_init); +module_exit (umt_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for HanfTek UMT 010 USB2.0 DVB-T device"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/vp7045-fe.c b/drivers/media/dvb/dvb-usb/vp7045-fe.c new file mode 100644 index 000000000000..2746edfeccba --- /dev/null +++ b/drivers/media/dvb/dvb-usb/vp7045-fe.c @@ -0,0 +1,196 @@ +/* DVB frontend part of the Linux driver for TwinhanDTV Alpha/MagicBoxII USB2.0 + * DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + * + */ +#include "vp7045.h" + +/* It is a Zarlink MT352 within a Samsung Tuner (DNOS404ZH102A) - 040929 - AAT + * + * Programming is hidden inside the firmware, so set_frontend is very easy. + * Even though there is a Firmware command that one can use to access the demod + * via its registers. This is used for status information. + */ + +struct vp7045_fe_state { + struct dvb_frontend fe; + struct dvb_usb_device *d; +}; + + +static int vp7045_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 s0 = vp7045_read_reg(state->d,0x00), + s1 = vp7045_read_reg(state->d,0x01), + s3 = vp7045_read_reg(state->d,0x03); + + *status = 0; + if (s0 & (1 << 4)) + *status |= FE_HAS_CARRIER; + if (s0 & (1 << 1)) + *status |= FE_HAS_VITERBI; + if (s0 & (1 << 5)) + *status |= FE_HAS_LOCK; + if (s1 & (1 << 1)) + *status |= FE_HAS_SYNC; + if (s3 & (1 << 6)) + *status |= FE_HAS_SIGNAL; + + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} + +static int vp7045_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + *ber = (vp7045_read_reg(state->d, 0x0D) << 16) | + (vp7045_read_reg(state->d, 0x0E) << 8) | + vp7045_read_reg(state->d, 0x0F); + return 0; +} + +static int vp7045_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + *unc = (vp7045_read_reg(state->d, 0x10) << 8) | + vp7045_read_reg(state->d, 0x11); + return 0; +} + +static int vp7045_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u16 signal = (vp7045_read_reg(state->d, 0x14) << 8) | + vp7045_read_reg(state->d, 0x15); + + *strength = ~signal; + return 0; +} + +static int vp7045_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 _snr = vp7045_read_reg(state->d, 0x09); + *snr = (_snr << 8) | _snr; + return 0; +} + +static int vp7045_fe_init(struct dvb_frontend* fe) +{ + return 0; +} + +static int vp7045_fe_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int vp7045_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int vp7045_fe_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 buf[5]; + u32 freq = fep->frequency / 1000; + + buf[0] = (freq >> 16) & 0xff; + buf[1] = (freq >> 8) & 0xff; + buf[2] = freq & 0xff; + buf[3] = 0; + + switch (fep->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: buf[4] = 8; break; + case BANDWIDTH_7_MHZ: buf[4] = 7; break; + case BANDWIDTH_6_MHZ: buf[4] = 6; break; + case BANDWIDTH_AUTO: return -EOPNOTSUPP; + default: + return -EINVAL; + } + + vp7045_usb_op(state->d,LOCK_TUNER_COMMAND,buf,5,NULL,0,200); + return 0; +} + +static int vp7045_fe_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + return 0; +} + +static void vp7045_fe_release(struct dvb_frontend* fe) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops vp7045_fe_ops; + +struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d) +{ + struct vp7045_fe_state *s = kmalloc(sizeof(struct vp7045_fe_state), GFP_KERNEL); + if (s == NULL) + goto error; + memset(s,0,sizeof(struct vp7045_fe_state)); + + s->d = d; + s->fe.ops = &vp7045_fe_ops; + s->fe.demodulator_priv = s; + + goto success; +error: + return NULL; +success: + return &s->fe; +} + + +static struct dvb_frontend_ops vp7045_fe_ops = { + .info = { + .name = "Twinhan VP7045/46 USB DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = vp7045_fe_release, + + .init = vp7045_fe_init, + .sleep = vp7045_fe_sleep, + + .set_frontend = vp7045_fe_set_frontend, + .get_frontend = vp7045_fe_get_frontend, + .get_tune_settings = vp7045_fe_get_tune_settings, + + .read_status = vp7045_fe_read_status, + .read_ber = vp7045_fe_read_ber, + .read_signal_strength = vp7045_fe_read_signal_strength, + .read_snr = vp7045_fe_read_snr, + .read_ucblocks = vp7045_fe_read_unc_blocks, +}; diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c new file mode 100644 index 000000000000..02ecc9a8e3b6 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -0,0 +1,263 @@ +/* DVB USB compliant Linux driver for the + * - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver + * - DigitalNow TinyUSB2 DVB-t receiver + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "vp7045.h" + +/* debug */ +int dvb_usb_vp7045_debug; +module_param_named(debug,dvb_usb_vp7045_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); + +int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) +{ + int ret = 0; + u8 inbuf[12] = { 0 }, outbuf[20] = { 0 }; + + outbuf[0] = cmd; + + if (outlen > 19) + outlen = 19; + + if (inlen > 11) + inlen = 11; + + if (out != NULL && outlen > 0) + memcpy(&outbuf[1], out, outlen); + + deb_xfer("out buffer: "); + debug_dump(outbuf,outlen+1,deb_xfer); + + if ((ret = down_interruptible(&d->usb_sem))) + return ret; + + if (usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev,0), + TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, + outbuf, 20, 2*HZ) != 20) { + err("USB control message 'out' went wrong."); + ret = -EIO; + goto unlock; + } + + msleep(msec); + + if (usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev,0), + TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, + inbuf, 12, 2*HZ) != 12) { + err("USB control message 'in' went wrong."); + ret = -EIO; + goto unlock; + } + + deb_xfer("in buffer: "); + debug_dump(inbuf,12,deb_xfer); + + if (in != NULL && inlen > 0) + memcpy(in,&inbuf[1],inlen); + +unlock: + up(&d->usb_sem); + + return ret; +} + +u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) +{ + u8 obuf[2] = { 0 },v; + obuf[1] = reg; + + vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); + + return v; +} + +static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 v = onoff; + return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); +} + +/* remote control stuff */ + +/* The keymapping struct. Somehow this should be loaded to the driver, but + * currently it is hardcoded. */ +static struct dvb_usb_rc_key vp7045_rc_keys[] = { + /* insert the keys like this. to make the raw keys visible, enable + * debug=0x04 when loading dvb-usb-vp7045. */ + + /* these keys are probably wrong. I don't have a working IR-receiver on my + * vp7045, so I can't test it. Patches are welcome. */ + { 0x00, 0x01, KEY_1 }, + { 0x00, 0x02, KEY_2 }, +}; + +static int vp7045_rc_query(struct dvb_usb_device *d, u32 *key_buf, int *state) +{ + u8 key; + int i; + vp7045_usb_op(d,RC_VAL_READ,NULL,0,&key,1,20); + + deb_rc("remote query key: %x %d\n",key,key); + + if (key == 0x44) { + *state = REMOTE_NO_KEY_PRESSED; + return 0; + } + + for (i = 0; i < sizeof(vp7045_rc_keys)/sizeof(struct dvb_usb_rc_key); i++) + if (vp7045_rc_keys[i].data == key) { + *state = REMOTE_KEY_PRESSED; + *key_buf = vp7045_rc_keys[i].event; + break; + } + return 0; +} + +static int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) +{ + int i = 0; + u8 v,br[2]; + for (i=0; i < len; i++) { + v = offset + i; + vp7045_usb_op(d,GET_EE_VALUE,&v,1,br,2,5); + buf[i] = br[1]; + } + deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ",offset, i); + debug_dump(buf,i,deb_info); + return 0; +} + + +static int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) +{ + return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR); +} + +static int vp7045_frontend_attach(struct dvb_usb_device *d) +{ + u8 buf[255] = { 0 }; + + vp7045_usb_op(d,VENDOR_STRING_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("firmware says: %s ",buf); + + vp7045_usb_op(d,PRODUCT_STRING_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("%s ",buf); + + vp7045_usb_op(d,FW_VERSION_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("v%s\n",buf); + +/* Dump the EEPROM */ +/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ + + d->fe = vp7045_fe_attach(d); + + return 0; +} + +static struct dvb_usb_properties vp7045_properties; + +static int vp7045_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&vp7045_properties,THIS_MODULE); +} + +static struct usb_device_id vp7045_usb_table [] = { + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_COLD) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_WARM) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_COLD) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_WARM) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(usb, vp7045_usb_table); + +static struct dvb_usb_properties vp7045_properties = { + .caps = 0, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-vp7045-01.fw", + + .power_ctrl = vp7045_power_ctrl, + .frontend_attach = vp7045_frontend_attach, + .read_mac_address = vp7045_read_mac_addr, + + .rc_interval = 400, + .rc_key_map = vp7045_rc_keys, + .rc_key_map_size = ARRAY_SIZE(vp7045_rc_keys), + .rc_query = vp7045_rc_query, + + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 2, + .devices = { + { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", + .cold_ids = { &vp7045_usb_table[0], NULL }, + .warm_ids = { &vp7045_usb_table[1], NULL }, + }, + { .name = "DigitalNow TinyUSB 2 DVB-t Receiver", + .cold_ids = { &vp7045_usb_table[2], NULL }, + .warm_ids = { &vp7045_usb_table[3], NULL }, + }, + { 0 }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver vp7045_usb_driver = { + .owner = THIS_MODULE, + .name = "dvb-usb-vp7045", + .probe = vp7045_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = vp7045_usb_table, +}; + +/* module stuff */ +static int __init vp7045_usb_module_init(void) +{ + int result; + if ((result = usb_register(&vp7045_usb_driver))) { + err("usb_register failed. (%d)",result); + return result; + } + + return 0; +} + +static void __exit vp7045_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&vp7045_usb_driver); +} + +module_init(vp7045_usb_module_init); +module_exit(vp7045_usb_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/vp7045.h b/drivers/media/dvb/dvb-usb/vp7045.h new file mode 100644 index 000000000000..9ce21a20fa86 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/vp7045.h @@ -0,0 +1,78 @@ +/* Common header-file of the Linux driver for the TwinhanDTV Alpha/MagicBoxII + * USB2.0 DVB-T receiver. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * Thanks to Twinhan who kindly provided hardware and information. + * + * 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, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_VP7045_H_ +#define _DVB_USB_VP7045_H_ + +#define DVB_USB_LOG_PREFIX "vp7045" +#include "dvb-usb.h" + +extern int dvb_usb_vp7045_debug; +#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) +#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args) + +/* vp7045 commands */ + +/* Twinhan Vendor requests */ +#define TH_COMMAND_IN 0xC0 +#define TH_COMMAND_OUT 0xC1 + +/* command bytes */ +#define TUNER_REG_READ 0x03 +#define TUNER_REG_WRITE 0x04 + +#define RC_VAL_READ 0x05 + #define RC_NO_KEY 0x44 + +#define SET_TUNER_POWER 0x06 +#define CHECK_TUNER_POWER 0x12 + #define Tuner_Power_ON 1 + #define Tuner_Power_OFF 0 + +#define GET_USB_SPEED 0x07 + #define USB_SPEED_LOW 0 + #define USB_SPEED_FULL 1 + #define USB_SPEED_HIGH 2 + +#define LOCK_TUNER_COMMAND 0x09 + +#define TUNER_SIGNAL_READ 0x0A + +/* FX2 eeprom */ +#define SET_EE_VALUE 0x10 +#define GET_EE_VALUE 0x11 + #define FX2_ID_ADDR 0x00 + #define VID_MSB_ADDR 0x02 + #define VID_LSB_ADDR 0x01 + #define PID_MSB_ADDR 0x04 + #define PID_LSB_ADDR 0x03 + #define MAC_0_ADDR 0x07 + #define MAC_1_ADDR 0x08 + #define MAC_2_ADDR 0x09 + #define MAC_3_ADDR 0x0a + #define MAC_4_ADDR 0x0b + #define MAC_5_ADDR 0x0c + +#define RESET_FX2 0x13 + +#define FW_VERSION_READ 0x0B +#define VENDOR_STRING_READ 0x0C +#define PRODUCT_STRING_READ 0x0D +#define FW_BCD_VERSION_READ 0x14 + +extern struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d); +extern int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen,int msec); +extern u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg); + +#endif diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 75fb556ec01f..b4fddf513ebe 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -173,4 +173,12 @@ config DVB_OR51132 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_BCM3510 + tristate "Broadcom BCM3510" + depends on DVB_CORE + select FW_LOADER + help + An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to + support this frontend. + endmenu diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 7f8784870eab..91d6d3576d3d 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_DVB_STV0297) += stv0297.o obj-$(CONFIG_DVB_NXT2002) += nxt2002.o obj-$(CONFIG_DVB_OR51211) += or51211.o obj-$(CONFIG_DVB_OR51132) += or51132.o +obj-$(CONFIG_DVB_BCM3510) += bcm3510.o diff --git a/drivers/media/dvb/frontends/bcm3510.c b/drivers/media/dvb/frontends/bcm3510.c new file mode 100644 index 000000000000..f5fdc5c3e605 --- /dev/null +++ b/drivers/media/dvb/frontends/bcm3510.c @@ -0,0 +1,853 @@ +/* + * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) + * + * Copyright (C) 2001-5, B2C2 inc. + * + * GPL/Linux driver written by Patrick Boettcher + * + * This driver is "hard-coded" to be used with the 1st generation of + * Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming + * (Panasonic CT10S) is located here, which is actually wrong. Unless there is + * another device with a BCM3510, this is no problem. + * + * The driver works also with QAM64 DVB-C, but had an unreasonable high + * UNC. (Tested with the Air2PC ATSC 1st generation) + * + * You'll need a firmware for this driver in order to get it running. It is + * called "dvb-fe-bcm3510-01.fw". + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 675 Mass + * Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "bcm3510.h" +#include "bcm3510_priv.h" + +struct bcm3510_state { + + struct i2c_adapter* i2c; + struct dvb_frontend_ops ops; + const struct bcm3510_config* config; + struct dvb_frontend frontend; + + /* demodulator private data */ + struct semaphore hab_sem; + u8 firmware_loaded:1; + + unsigned long next_status_check; + unsigned long status_check_interval; + struct bcm3510_hab_cmd_status1 status1; + struct bcm3510_hab_cmd_status2 status2; +}; + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able))."); + +#define dprintk(level,x...) if (level & debug) printk(x) +#define dbufout(b,l,m) {\ + int i; \ + for (i = 0; i < l; i++) \ + m("%02x ",b[i]); \ +} +#define deb_info(args...) dprintk(0x01,args) +#define deb_i2c(args...) dprintk(0x02,args) +#define deb_hab(args...) dprintk(0x04,args) + +/* transfer functions */ +static int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) +{ + u8 b[256]; + int err; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 }; + + b[0] = reg; + memcpy(&b[1],buf,len); + + deb_i2c("i2c wr %02x: ",reg); + dbufout(buf,len,deb_i2c); + deb_i2c("\n"); + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + + deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n", + __FUNCTION__, state->config->demod_address, reg, err); + return -EREMOTEIO; + } + + return 0; +} + +static int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) +{ + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } + }; + int err; + + memset(buf,0,len); + + if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { + deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n", + __FUNCTION__, state->config->demod_address, reg, err); + return -EREMOTEIO; + } + deb_i2c("i2c rd %02x: ",reg); + dbufout(buf,len,deb_i2c); + deb_i2c("\n"); + + return 0; +} + +static int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v) +{ + return bcm3510_writebytes(state,reg,&v.raw,1); +} + +static int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v) +{ + return bcm3510_readbytes(state,reg,&v->raw,1); +} + +/* Host Access Buffer transfers */ +static int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len) +{ + bcm3510_register_value v; + int ret,i; + + v.HABADR_a6.HABADR = 0; + if ((ret = bcm3510_writeB(st,0xa6,v)) < 0) + return ret; + + for (i = 0; i < len; i++) { + if ((ret = bcm3510_readB(st,0xa7,&v)) < 0) + return ret; + buf[i] = v.HABDATA_a7; + } + return 0; +} + +static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len) +{ + bcm3510_register_value v,hab; + int ret,i; + unsigned long t; + +/* Check if any previous HAB request still needs to be serviced by the + * Aquisition Processor before sending new request */ + if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) + return ret; + if (v.HABSTAT_a8.HABR) { + deb_info("HAB is running already - clearing it.\n"); + v.HABSTAT_a8.HABR = 0; + bcm3510_writeB(st,0xa8,v); +// return -EBUSY; + } + +/* Send the start HAB Address (automatically incremented after write of + * HABDATA) and write the HAB Data */ + hab.HABADR_a6.HABADR = 0; + if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0) + return ret; + + for (i = 0; i < len; i++) { + hab.HABDATA_a7 = buf[i]; + if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0) + return ret; + } + +/* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to + * be written) */ + v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1; + if ((ret = bcm3510_writeB(st,0xa8,v)) < 0) + return ret; + +/* Polling method: Wait until the AP finishes processing the HAB request */ + t = jiffies + 1*HZ; + while (time_before(jiffies, t)) { + deb_info("waiting for HAB to complete\n"); + msleep(10); + if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) + return ret; + + if (!v.HABSTAT_a8.HABR) + return 0; + } + + deb_info("send_request execution timed out.\n"); + return -ETIMEDOUT; +} + +static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen) +{ + u8 ob[olen+2],ib[ilen+2]; + int ret = 0; + + ob[0] = cmd; + ob[1] = msgid; + memcpy(&ob[2],obuf,olen); + + deb_hab("hab snd: "); + dbufout(ob,olen+2,deb_hab); + deb_hab("\n"); + + if (down_interruptible(&st->hab_sem) < 0) + return -EAGAIN; + + if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 || + (ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0) + goto error; + + deb_hab("hab get: "); + dbufout(ib,ilen+2,deb_hab); + deb_hab("\n"); + + memcpy(ibuf,&ib[2],ilen); +error: + up(&st->hab_sem); + return ret; +} + +#if 0 +/* not needed, we use a semaphore to prevent HAB races */ +static int bcm3510_is_ap_ready(struct bcm3510_state *st) +{ + bcm3510_register_value ap,hab; + int ret; + + if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 || + (ret = bcm3510_readB(st,0xa2,&ap) < 0)) + return ret; + + if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) { + deb_info("AP is busy\n"); + return -EBUSY; + } + + return 0; +} +#endif + +static int bcm3510_bert_reset(struct bcm3510_state *st) +{ + bcm3510_register_value b; + int ret; + + if ((ret < bcm3510_readB(st,0xfa,&b)) < 0) + return ret; + + b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); + b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b); + b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); + b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b); + + /* clear residual bit counter TODO */ + return 0; +} + +static int bcm3510_refresh_state(struct bcm3510_state *st) +{ + if (time_after(jiffies,st->next_status_check)) { + bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1)); + bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2)); + st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; + } + return 0; +} + +static int bcm3510_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + + *status = 0; + if (st->status1.STATUS1.RECEIVER_LOCK) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + + if (st->status1.STATUS1.FEC_LOCK) + *status |= FE_HAS_VITERBI; + + if (st->status1.STATUS1.OUT_PLL_LOCK) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + if (*status & FE_HAS_LOCK) + st->status_check_interval = 1500; + else /* more frequently checks if no lock has been achieved yet */ + st->status_check_interval = 500; + + deb_info("real_status: %02x\n",*status); + return 0; +} + +static int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + + *ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2; + return 0; +} + +static int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + *unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1; + return 0; +} + +static int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct bcm3510_state* st = fe->demodulator_priv; + s32 t; + + bcm3510_refresh_state(st); + t = st->status2.SIGNAL; + + if (t > 190) + t = 190; + if (t < 90) + t = 90; + + t -= 90; + t = t * 0xff / 100; + /* normalize if necessary */ + *strength = (t << 8) | t; + return 0; +} + +static int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_refresh_state(st); + + *snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8); + return 0; +} + +/* tuner frontend programming */ +static int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a) +{ + struct bcm3510_hab_cmd_tune c; + memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune)); + +/* I2C Mode disabled, set 16 control / Data pairs */ + c.length = 0x10; + c.clock_width = 0; +/* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to + * logic high (as Configuration) */ + c.misc = 0x10; +/* Set duration of the initial state of TUNCTL = 3.34 micro Sec */ + c.TUNCTL_state = 0x40; + +/* PRESCALER DEVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */ + c.ctl_dat[0].ctrl.size = BITS_8; + c.ctl_dat[0].data = 0x80 | bc; + +/* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */ + c.ctl_dat[1].ctrl.size = BITS_8; + c.ctl_dat[1].data = 4; + +/* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */ + c.ctl_dat[2].ctrl.size = BITS_3; + c.ctl_dat[2].data = 0x20; + +/* control CS0 pin, pulse byte ? */ + c.ctl_dat[3].ctrl.size = BITS_3; + c.ctl_dat[3].ctrl.clk_off = 1; + c.ctl_dat[3].ctrl.cs0 = 1; + c.ctl_dat[3].data = 0x40; + +/* PGM_S18 to PGM_S11 */ + c.ctl_dat[4].ctrl.size = BITS_8; + c.ctl_dat[4].data = n >> 3; + +/* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */ + c.ctl_dat[5].ctrl.size = BITS_8; + c.ctl_dat[5].data = ((n & 0x7) << 5) | (a >> 2); + +/* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */ + c.ctl_dat[6].ctrl.size = BITS_3; + c.ctl_dat[6].data = (a << 6) & 0xdf; + +/* control CS0 pin, pulse byte ? */ + c.ctl_dat[7].ctrl.size = BITS_3; + c.ctl_dat[7].ctrl.clk_off = 1; + c.ctl_dat[7].ctrl.cs0 = 1; + c.ctl_dat[7].data = 0x40; + +/* PRESCALER DEVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */ + c.ctl_dat[8].ctrl.size = BITS_8; + c.ctl_dat[8].data = 0x80; + +/* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */ + c.ctl_dat[9].ctrl.size = BITS_8; + c.ctl_dat[9].data = 0x10; + +/* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */ + c.ctl_dat[10].ctrl.size = BITS_3; + c.ctl_dat[10].data = 0x20; + +/* pulse byte */ + c.ctl_dat[11].ctrl.size = BITS_3; + c.ctl_dat[11].ctrl.clk_off = 1; + c.ctl_dat[11].ctrl.cs1 = 1; + c.ctl_dat[11].data = 0x40; + +/* PGM_S18 to PGM_S11 */ + c.ctl_dat[12].ctrl.size = BITS_8; + c.ctl_dat[12].data = 0x2a; + +/* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */ + c.ctl_dat[13].ctrl.size = BITS_8; + c.ctl_dat[13].data = 0x8e; + +/* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */ + c.ctl_dat[14].ctrl.size = BITS_3; + c.ctl_dat[14].data = 0; + +/* Pulse Byte */ + c.ctl_dat[15].ctrl.size = BITS_3; + c.ctl_dat[15].ctrl.clk_off = 1; + c.ctl_dat[15].ctrl.cs1 = 1; + c.ctl_dat[15].data = 0x40; + + return bcm3510_do_hab_cmd(st,CMD_TUNE, MSGID_TUNE,(u8 *) &c,sizeof(c), NULL, 0); +} + +static int bcm3510_set_freq(struct bcm3510_state* st,u32 freq) +{ + u8 bc,a; + u16 n; + s32 YIntercept,Tfvco1; + + freq /= 1000; + + deb_info("%dkHz:",freq); + /* set Band Switch */ + if (freq <= 168000) + bc = 0x1c; + else if (freq <= 378000) + bc = 0x2c; + else + bc = 0x30; + + if (freq >= 470000) { + freq -= 470001; + YIntercept = 18805; + } else if (freq >= 90000) { + freq -= 90001; + YIntercept = 15005; + } else if (freq >= 76000){ + freq -= 76001; + YIntercept = 14865; + } else { + freq -= 54001; + YIntercept = 14645; + } + + Tfvco1 = (((freq/6000)*60 + YIntercept)*4)/10; + + n = Tfvco1 >> 6; + a = Tfvco1 & 0x3f; + + deb_info(" BC1_2_3_4: %x, N: %x A: %x\n", bc, n, a); + if (n >= 16 && n <= 2047) + return bcm3510_tuner_cmd(st,bc,n,a); + + return -EINVAL; +} + +static int bcm3510_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) +{ + struct bcm3510_state* st = fe->demodulator_priv; + struct bcm3510_hab_cmd_ext_acquire cmd; + struct bcm3510_hab_cmd_bert_control bert; + int ret; + + memset(&cmd,0,sizeof(cmd)); + switch (p->u.vsb.modulation) { + case QAM_256: + cmd.ACQUIRE0.MODE = 0x1; + cmd.ACQUIRE1.SYM_RATE = 0x1; + cmd.ACQUIRE1.IF_FREQ = 0x1; + break; + case QAM_64: + cmd.ACQUIRE0.MODE = 0x2; + cmd.ACQUIRE1.SYM_RATE = 0x2; + cmd.ACQUIRE1.IF_FREQ = 0x1; + break; +/* case QAM_256: + cmd.ACQUIRE0.MODE = 0x3; + break; + case QAM_128: + cmd.ACQUIRE0.MODE = 0x4; + break; + case QAM_64: + cmd.ACQUIRE0.MODE = 0x5; + break; + case QAM_32: + cmd.ACQUIRE0.MODE = 0x6; + break; + case QAM_16: + cmd.ACQUIRE0.MODE = 0x7; + break;*/ + case VSB_8: + cmd.ACQUIRE0.MODE = 0x8; + cmd.ACQUIRE1.SYM_RATE = 0x0; + cmd.ACQUIRE1.IF_FREQ = 0x0; + break; + case VSB_16: + cmd.ACQUIRE0.MODE = 0x9; + cmd.ACQUIRE1.SYM_RATE = 0x0; + cmd.ACQUIRE1.IF_FREQ = 0x0; + default: + return -EINVAL; + }; + cmd.ACQUIRE0.OFFSET = 0; + cmd.ACQUIRE0.NTSCSWEEP = 1; + cmd.ACQUIRE0.FA = 1; + cmd.ACQUIRE0.BW = 0; + +/* if (enableOffset) { + cmd.IF_OFFSET0 = xx; + cmd.IF_OFFSET1 = xx; + + cmd.SYM_OFFSET0 = xx; + cmd.SYM_OFFSET1 = xx; + if (enableNtscSweep) { + cmd.NTSC_OFFSET0; + cmd.NTSC_OFFSET1; + } + } */ + bcm3510_do_hab_cmd(st, CMD_ACQUIRE, MSGID_EXT_TUNER_ACQUIRE, (u8 *) &cmd, sizeof(cmd), NULL, 0); + +/* doing it with different MSGIDs, data book and source differs */ + bert.BE = 0; + bert.unused = 0; + bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_CONTROL, (u8 *) &bert, sizeof(bert), NULL, 0); + bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_SET, (u8 *) &bert, sizeof(bert), NULL, 0); + + bcm3510_bert_reset(st); + + if ((ret = bcm3510_set_freq(st,p->frequency)) < 0) + return ret; + + memset(&st->status1,0,sizeof(st->status1)); + memset(&st->status2,0,sizeof(st->status2)); + st->status_check_interval = 500; + +/* Give the AP some time */ + msleep(200); + + return 0; +} + +static int bcm3510_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int bcm3510_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + s->step_size = 0; + s->max_drift = 0; + return 0; +} + +static void bcm3510_release(struct dvb_frontend* fe) +{ + struct bcm3510_state* state = fe->demodulator_priv; + kfree(state); +} + +/* firmware download: + * firmware file is build up like this: + * 16bit addr, 16bit length, 8byte of length + */ +#define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw" + +static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, u8 *b, u16 len) +{ + int ret = 0,i; + bcm3510_register_value vH, vL,vD; + + vH.MADRH_a9 = addr >> 8; + vL.MADRL_aa = addr; + if ((ret = bcm3510_writeB(st,0xa9,vH)) < 0) return ret; + if ((ret = bcm3510_writeB(st,0xaa,vL)) < 0) return ret; + + for (i = 0; i < len; i++) { + vD.MDATA_ab = b[i]; + if ((ret = bcm3510_writeB(st,0xab,vD)) < 0) + return ret; + } + + return 0; +} + +static int bcm3510_download_firmware(struct dvb_frontend* fe) +{ + struct bcm3510_state* st = fe->demodulator_priv; + const struct firmware *fw; + u16 addr,len; + u8 *b; + int ret,i; + + deb_info("requesting firmware\n"); + if ((ret = st->config->request_firmware(fe, &fw, BCM3510_DEFAULT_FIRMWARE)) < 0) { + err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); + return ret; + } + deb_info("got firmware: %d\n",fw->size); + + b = fw->data; + for (i = 0; i < fw->size;) { + addr = le16_to_cpu( *( (u16 *)&b[i] ) ); + len = le16_to_cpu( *( (u16 *)&b[i+2] ) ); + deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04x\n",addr,len,fw->size); + if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { + err("firmware download failed: %d\n",ret); + return ret; + } + i += 4 + len; + } + release_firmware(fw); + deb_info("firmware download successfully completed\n"); + return 0; +} + +static int bcm3510_check_firmware_version(struct bcm3510_state *st) +{ + struct bcm3510_hab_cmd_get_version_info ver; + bcm3510_do_hab_cmd(st,CMD_GET_VERSION_INFO,MSGID_GET_VERSION_INFO,NULL,0,(u8*)&ver,sizeof(ver)); + + deb_info("Version information: 0x%02x 0x%02x 0x%02x 0x%02x\n", + ver.microcode_version, ver.script_version, ver.config_version, ver.demod_version); + + if (ver.script_version == BCM3510_DEF_SCRIPT_VERSION && + ver.config_version == BCM3510_DEF_CONFIG_VERSION && + ver.demod_version == BCM3510_DEF_DEMOD_VERSION) + return 0; + + deb_info("version check failed\n"); + return -ENODEV; +} + +/* (un)resetting the AP */ +static int bcm3510_reset(struct bcm3510_state *st) +{ + int ret; + unsigned long t; + bcm3510_register_value v; + + bcm3510_readB(st,0xa0,&v); v.HCTL1_a0.RESET = 1; + if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) + return ret; + + t = jiffies + 3*HZ; + while (time_before(jiffies, t)) { + msleep(10); + if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) + return ret; + + if (v.APSTAT1_a2.RESET) + return 0; + } + deb_info("reset timed out\n"); + return -ETIMEDOUT; +} + +static int bcm3510_clear_reset(struct bcm3510_state *st) +{ + bcm3510_register_value v; + int ret; + unsigned long t; + + v.raw = 0; + if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) + return ret; + + t = jiffies + 3*HZ; + while (time_before(jiffies, t)) { + msleep(10); + if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) + return ret; + + /* verify that reset is cleared */ + if (!v.APSTAT1_a2.RESET) + return 0; + } + deb_info("reset clear timed out\n"); + return -ETIMEDOUT; +} + +static int bcm3510_init_cold(struct bcm3510_state *st) +{ + int ret; + bcm3510_register_value v; + + /* read Acquisation Processor status register and check it is not in RUN mode */ + if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) + return ret; + if (v.APSTAT1_a2.RUN) { + deb_info("AP is already running - firmware already loaded.\n"); + return 0; + } + + deb_info("reset?\n"); + if ((ret = bcm3510_reset(st)) < 0) + return ret; + + deb_info("tristate?\n"); + /* tri-state */ + v.TSTCTL_2e.CTL = 0; + if ((ret = bcm3510_writeB(st,0x2e,v)) < 0) + return ret; + + deb_info("firmware?\n"); + if ((ret = bcm3510_download_firmware(&st->frontend)) < 0 || + (ret = bcm3510_clear_reset(st)) < 0) + return ret; + + /* anything left here to Let the acquisition processor begin execution at program counter 0000 ??? */ + + return 0; +} + +static int bcm3510_init(struct dvb_frontend* fe) +{ + struct bcm3510_state* st = fe->demodulator_priv; + bcm3510_register_value j; + struct bcm3510_hab_cmd_set_agc c; + int ret; + + if ((ret = bcm3510_readB(st,0xca,&j)) < 0) + return ret; + + deb_info("JDEC: %02x\n",j.raw); + + switch (j.JDEC_ca.JDEC) { + case JDEC_WAIT_AT_RAM: + deb_info("attempting to download firmware\n"); + if ((ret = bcm3510_init_cold(st)) < 0) + return ret; + case JDEC_EEPROM_LOAD_WAIT: /* fall-through is wanted */ + deb_info("firmware is loaded\n"); + bcm3510_check_firmware_version(st); + break; + default: + return -ENODEV; + } + + memset(&c,0,1); + c.SEL = 1; + bcm3510_do_hab_cmd(st,CMD_AUTO_PARAM,MSGID_SET_RF_AGC_SEL,(u8 *)&c,sizeof(c),NULL,0); + + return 0; +} + + +static struct dvb_frontend_ops bcm3510_ops; + +struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, + struct i2c_adapter *i2c) +{ + struct bcm3510_state* state = NULL; + int ret; + bcm3510_register_value v; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct bcm3510_state), GFP_KERNEL); + if (state == NULL) + goto error; + memset(state,0,sizeof(struct bcm3510_state)); + + /* setup the state */ + + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &bcm3510_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + + sema_init(&state->hab_sem, 1); + + if ((ret = bcm3510_readB(state,0xe0,&v)) < 0) + goto error; + + deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER); + + if ((v.REVID_e0.REV != 0x1 && v.REVID_e0.LAYER != 0xb) && /* cold */ + (v.REVID_e0.REV != 0x8 && v.REVID_e0.LAYER != 0x0)) /* warm */ + goto error; + + info("Revision: 0x%1x, Layer: 0x%1x.",v.REVID_e0.REV,v.REVID_e0.LAYER); + + bcm3510_reset(state); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(bcm3510_attach); + +static struct dvb_frontend_ops bcm3510_ops = { + + .info = { + .name = "Broadcom BCM3510 VSB/QAM frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 803000000, + /* stepsize is just a guess */ + .frequency_stepsize = 0, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_8VSB | FE_CAN_16VSB | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 + }, + + .release = bcm3510_release, + + .init = bcm3510_init, + .sleep = bcm3510_sleep, + + .set_frontend = bcm3510_set_frontend, + .get_tune_settings = bcm3510_get_tune_settings, + + .read_status = bcm3510_read_status, + .read_ber = bcm3510_read_ber, + .read_signal_strength = bcm3510_read_signal_strength, + .read_snr = bcm3510_read_snr, + .read_ucblocks = bcm3510_read_unc, +}; + +MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver"); +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/bcm3510.h b/drivers/media/dvb/frontends/bcm3510.h new file mode 100644 index 000000000000..80f5d0953d02 --- /dev/null +++ b/drivers/media/dvb/frontends/bcm3510.h @@ -0,0 +1,40 @@ +/* + * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) + * + * Copyright (C) 2001-5, B2C2 inc. + * + * GPL/Linux driver written by Patrick Boettcher + * + * 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. + */ +#ifndef BCM3510_H +#define BCM3510_H + +#include +#include + +struct bcm3510_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, + struct i2c_adapter* i2c); + +#endif diff --git a/drivers/media/dvb/frontends/bcm3510_priv.h b/drivers/media/dvb/frontends/bcm3510_priv.h new file mode 100644 index 000000000000..3bb1bc2a04f0 --- /dev/null +++ b/drivers/media/dvb/frontends/bcm3510_priv.h @@ -0,0 +1,460 @@ +/* + * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) + * + * Copyright (C) 2001-5, B2C2 inc. + * + * GPL/Linux driver written by Patrick Boettcher + * + * 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. + */ +#ifndef __BCM3510_PRIV_H__ +#define __BCM3510_PRIV_H__ + +#define PACKED __attribute__((packed)) + +#undef err +#define err(format, arg...) printk(KERN_ERR "bcm3510: " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO "bcm3510: " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING "bcm3510: " format "\n" , ## arg) + + +#define PANASONIC_FIRST_IF_BASE_IN_KHz 1407500 +#define BCM3510_SYMBOL_RATE 5381000 + +typedef union { + u8 raw; + + struct { + u8 CTL :8; + } TSTCTL_2e; + + u8 LDCERC_4e; + u8 LDUERC_4f; + u8 LD_BER0_65; + u8 LD_BER1_66; + u8 LD_BER2_67; + u8 LD_BER3_68; + + struct { + u8 RESET :1; + u8 IDLE :1; + u8 STOP :1; + u8 HIRQ0 :1; + u8 HIRQ1 :1; + u8 na0 :1; + u8 HABAV :1; + u8 na1 :1; + } HCTL1_a0; + + struct { + u8 na0 :1; + u8 IDLMSK :1; + u8 STMSK :1; + u8 I0MSK :1; + u8 I1MSK :1; + u8 na1 :1; + u8 HABMSK :1; + u8 na2 :1; + } HCTLMSK_a1; + + struct { + u8 RESET :1; + u8 IDLE :1; + u8 STOP :1; + u8 RUN :1; + u8 HABAV :1; + u8 MEMAV :1; + u8 ALDONE :1; + u8 REIRQ :1; + } APSTAT1_a2; + + struct { + u8 RSTMSK :1; + u8 IMSK :1; + u8 SMSK :1; + u8 RMSK :1; + u8 HABMSK :1; + u8 MAVMSK :1; + u8 ALDMSK :1; + u8 REMSK :1; + } APMSK1_a3; + + u8 APSTAT2_a4; + u8 APMSK2_a5; + + struct { + u8 HABADR :7; + u8 na :1; + } HABADR_a6; + + u8 HABDATA_a7; + + struct { + u8 HABR :1; + u8 LDHABR :1; + u8 APMSK :1; + u8 HMSK :1; + u8 LDMSK :1; + u8 na :3; + } HABSTAT_a8; + + u8 MADRH_a9; + u8 MADRL_aa; + u8 MDATA_ab; + + struct { +#define JDEC_WAIT_AT_RAM 0x7 +#define JDEC_EEPROM_LOAD_WAIT 0x4 + u8 JDEC :3; + u8 na :5; + } JDEC_ca; + + struct { + u8 REV :4; + u8 LAYER :4; + } REVID_e0; + + struct { + u8 unk0 :1; + u8 CNTCTL :1; + u8 BITCNT :1; + u8 unk1 :1; + u8 RESYNC :1; + u8 unk2 :3; + } BERCTL_fa; + + struct { + u8 CSEL0 :1; + u8 CLKED0 :1; + u8 CSEL1 :1; + u8 CLKED1 :1; + u8 CLKLEV :1; + u8 SPIVAR :1; + u8 na :2; + } TUNSET_fc; + + struct { + u8 CLK :1; + u8 DATA :1; + u8 CS0 :1; + u8 CS1 :1; + u8 AGCSEL :1; + u8 na0 :1; + u8 TUNSEL :1; + u8 na1 :1; + } TUNCTL_fd; + + u8 TUNSEL0_fe; + u8 TUNSEL1_ff; + +} bcm3510_register_value; + +/* HAB commands */ + +/* version */ +#define CMD_GET_VERSION_INFO 0x3D +#define MSGID_GET_VERSION_INFO 0x15 +struct bcm3510_hab_cmd_get_version_info { + u8 microcode_version; + u8 script_version; + u8 config_version; + u8 demod_version; +} PACKED; + +#define BCM3510_DEF_MICROCODE_VERSION 0x0E +#define BCM3510_DEF_SCRIPT_VERSION 0x06 +#define BCM3510_DEF_CONFIG_VERSION 0x01 +#define BCM3510_DEF_DEMOD_VERSION 0xB1 + +/* acquire */ +#define CMD_ACQUIRE 0x38 + +#define MSGID_EXT_TUNER_ACQUIRE 0x0A +struct bcm3510_hab_cmd_ext_acquire { + struct { + u8 MODE :4; + u8 BW :1; + u8 FA :1; + u8 NTSCSWEEP :1; + u8 OFFSET :1; + } PACKED ACQUIRE0; /* control_byte */ + + struct { + u8 IF_FREQ :3; + u8 zero0 :1; + u8 SYM_RATE :3; + u8 zero1 :1; + } PACKED ACQUIRE1; /* sym_if */ + + u8 IF_OFFSET0; /* IF_Offset_10hz */ + u8 IF_OFFSET1; + u8 SYM_OFFSET0; /* SymbolRateOffset */ + u8 SYM_OFFSET1; + u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ + u8 NTSC_OFFSET1; +} PACKED; + +#define MSGID_INT_TUNER_ACQUIRE 0x0B +struct bcm3510_hab_cmd_int_acquire { + struct { + u8 MODE :4; + u8 BW :1; + u8 FA :1; + u8 NTSCSWEEP :1; + u8 OFFSET :1; + } PACKED ACQUIRE0; /* control_byte */ + + struct { + u8 IF_FREQ :3; + u8 zero0 :1; + u8 SYM_RATE :3; + u8 zero1 :1; + } PACKED ACQUIRE1; /* sym_if */ + + u8 TUNER_FREQ0; + u8 TUNER_FREQ1; + u8 TUNER_FREQ2; + u8 TUNER_FREQ3; + u8 IF_OFFSET0; /* IF_Offset_10hz */ + u8 IF_OFFSET1; + u8 SYM_OFFSET0; /* SymbolRateOffset */ + u8 SYM_OFFSET1; + u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ + u8 NTSC_OFFSET1; +} PACKED; + +/* modes */ +#define BCM3510_QAM16 = 0x01 +#define BCM3510_QAM32 = 0x02 +#define BCM3510_QAM64 = 0x03 +#define BCM3510_QAM128 = 0x04 +#define BCM3510_QAM256 = 0x05 +#define BCM3510_8VSB = 0x0B +#define BCM3510_16VSB = 0x0D + +/* IF_FREQS */ +#define BCM3510_IF_TERRESTRIAL 0x0 +#define BCM3510_IF_CABLE 0x1 +#define BCM3510_IF_USE_CMD 0x7 + +/* SYM_RATE */ +#define BCM3510_SR_8VSB 0x0 /* 5381119 s/sec */ +#define BCM3510_SR_256QAM 0x1 /* 5360537 s/sec */ +#define BCM3510_SR_16QAM 0x2 /* 5056971 s/sec */ +#define BCM3510_SR_MISC 0x3 /* 5000000 s/sec */ +#define BCM3510_SR_USE_CMD 0x7 + +/* special symbol rate */ +#define CMD_SET_VALUE_NOT_LISTED 0x2d +#define MSGID_SET_SYMBOL_RATE_NOT_LISTED 0x0c +struct bcm3510_hab_cmd_set_sr_not_listed { + u8 HOST_SYM_RATE0; + u8 HOST_SYM_RATE1; + u8 HOST_SYM_RATE2; + u8 HOST_SYM_RATE3; +} PACKED; + +/* special IF */ +#define MSGID_SET_IF_FREQ_NOT_LISTED 0x0d +struct bcm3510_hab_cmd_set_if_freq_not_listed { + u8 HOST_IF_FREQ0; + u8 HOST_IF_FREQ1; + u8 HOST_IF_FREQ2; + u8 HOST_IF_FREQ3; +} PACKED; + +/* auto reacquire */ +#define CMD_AUTO_PARAM 0x2a +#define MSGID_AUTO_REACQUIRE 0x0e +struct bcm3510_hab_cmd_auto_reacquire { + u8 ACQ :1; /* on/off*/ + u8 unused :7; +} PACKED; + +#define MSGID_SET_RF_AGC_SEL 0x12 +struct bcm3510_hab_cmd_set_agc { + u8 LVL :1; + u8 unused :6; + u8 SEL :1; +} PACKED; + +#define MSGID_SET_AUTO_INVERSION 0x14 +struct bcm3510_hab_cmd_auto_inversion { + u8 AI :1; + u8 unused :7; +} PACKED; + + +/* bert control */ +#define CMD_STATE_CONTROL 0x12 +#define MSGID_BERT_CONTROL 0x0e +#define MSGID_BERT_SET 0xfa +struct bcm3510_hab_cmd_bert_control { + u8 BE :1; + u8 unused :7; +} PACKED; + +#define MSGID_TRI_STATE 0x2e +struct bcm3510_hab_cmd_tri_state { + u8 RE :1; /* a/d ram port pins */ + u8 PE :1; /* baud clock pin */ + u8 AC :1; /* a/d clock pin */ + u8 BE :1; /* baud clock pin */ + u8 unused :4; +} PACKED; + + +/* tune */ +#define CMD_TUNE 0x38 +#define MSGID_TUNE 0x16 +struct bcm3510_hab_cmd_tune_ctrl_data_pair { + struct { +#define BITS_8 0x07 +#define BITS_7 0x06 +#define BITS_6 0x05 +#define BITS_5 0x04 +#define BITS_4 0x03 +#define BITS_3 0x02 +#define BITS_2 0x01 +#define BITS_1 0x00 + u8 size :3; + u8 unk :2; + u8 clk_off :1; + u8 cs0 :1; + u8 cs1 :1; + + } PACKED ctrl; + + u8 data; +} PACKED; + +struct bcm3510_hab_cmd_tune { + u8 length; + u8 clock_width; + u8 misc; + u8 TUNCTL_state; + + struct bcm3510_hab_cmd_tune_ctrl_data_pair ctl_dat[16]; +} PACKED; + +#define CMD_STATUS 0x38 +#define MSGID_STATUS1 0x08 +struct bcm3510_hab_cmd_status1 { + struct { + u8 EQ_MODE :4; + u8 reserved :2; + u8 QRE :1; /* if QSE and the spectrum is inversed */ + u8 QSE :1; /* automatic spectral inversion */ + } PACKED STATUS0; + + struct { + u8 RECEIVER_LOCK :1; + u8 FEC_LOCK :1; + u8 OUT_PLL_LOCK :1; + u8 reserved :5; + } PACKED STATUS1; + + struct { + u8 reserved :2; + u8 BW :1; + u8 NTE :1; /* NTSC filter sweep enabled */ + u8 AQI :1; /* currently acquiring */ + u8 FA :1; /* fast acquisition */ + u8 ARI :1; /* auto reacquire */ + u8 TI :1; /* programming the tuner */ + } PACKED STATUS2; + u8 STATUS3; + u8 SNR_EST0; + u8 SNR_EST1; + u8 TUNER_FREQ0; + u8 TUNER_FREQ1; + u8 TUNER_FREQ2; + u8 TUNER_FREQ3; + u8 SYM_RATE0; + u8 SYM_RATE1; + u8 SYM_RATE2; + u8 SYM_RATE3; + u8 SYM_OFFSET0; + u8 SYM_OFFSET1; + u8 SYM_ERROR0; + u8 SYM_ERROR1; + u8 IF_FREQ0; + u8 IF_FREQ1; + u8 IF_FREQ2; + u8 IF_FREQ3; + u8 IF_OFFSET0; + u8 IF_OFFSET1; + u8 IF_ERROR0; + u8 IF_ERROR1; + u8 NTSC_FILTER0; + u8 NTSC_FILTER1; + u8 NTSC_FILTER2; + u8 NTSC_FILTER3; + u8 NTSC_OFFSET0; + u8 NTSC_OFFSET1; + u8 NTSC_ERROR0; + u8 NTSC_ERROR1; + u8 INT_AGC_LEVEL0; + u8 INT_AGC_LEVEL1; + u8 EXT_AGC_LEVEL0; + u8 EXT_AGC_LEVEL1; +} PACKED; + +#define MSGID_STATUS2 0x14 +struct bcm3510_hab_cmd_status2 { + struct { + u8 EQ_MODE :4; + u8 reserved :2; + u8 QRE :1; + u8 QSR :1; + } PACKED STATUS0; + struct { + u8 RL :1; + u8 FL :1; + u8 OL :1; + u8 reserved :5; + } PACKED STATUS1; + u8 SYMBOL_RATE0; + u8 SYMBOL_RATE1; + u8 SYMBOL_RATE2; + u8 SYMBOL_RATE3; + u8 LDCERC0; + u8 LDCERC1; + u8 LDCERC2; + u8 LDCERC3; + u8 LDUERC0; + u8 LDUERC1; + u8 LDUERC2; + u8 LDUERC3; + u8 LDBER0; + u8 LDBER1; + u8 LDBER2; + u8 LDBER3; + struct { + u8 MODE_TYPE :4; /* acquire mode 0 */ + u8 reservd :4; + } MODE_TYPE; + u8 SNR_EST0; + u8 SNR_EST1; + u8 SIGNAL; +} PACKED; + +#define CMD_SET_RF_BW_NOT_LISTED 0x3f +#define MSGID_SET_RF_BW_NOT_LISTED 0x11 +/* TODO */ + +#endif diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c index 47ab02e133d1..1a4f1f7c228a 100644 --- a/drivers/media/dvb/frontends/dib3000-common.c +++ b/drivers/media/dvb/frontends/dib3000-common.c @@ -73,7 +73,7 @@ u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ }; MODULE_AUTHOR("Patrick Boettcher config.pll_addr && state->config.pll_set) { - dib3000mb_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe)); - state->config.pll_set(fe, fep, NULL); - dib3000mb_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe)); + if (tuner && state->config.pll_set) { + state->config.pll_set(fe, fep); deb_setf("bandwidth: "); switch (ofdm->bandwidth) { @@ -389,11 +385,8 @@ static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF); - if (state->config.pll_init) { - dib3000mb_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe)); - state->config.pll_init(fe,NULL); - dib3000mb_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe)); - } + if (state->config.pll_init) + state->config.pll_init(fe); return 0; } @@ -623,7 +616,7 @@ static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) { struct dib3000_state* state = fe->demodulator_priv; - *unc = rd(DIB3000MB_REG_UNC); + *unc = rd(DIB3000MB_REG_PACKET_ERROR_RATE); return 0; } @@ -638,9 +631,6 @@ static int dib3000mb_sleep(struct dvb_frontend* fe) static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) { tune->min_delay_ms = 800; - tune->step_size = 166667; - tune->max_drift = 166667 * 2; - return 0; } diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h index 57e61aa5b07b..999b19047816 100644 --- a/drivers/media/dvb/frontends/dib3000mb_priv.h +++ b/drivers/media/dvb/frontends/dib3000mb_priv.h @@ -294,7 +294,7 @@ static u16 dib3000mb_reg_filter_coeffs[] = { static u16 dib3000mb_filter_coeffs[] = { 226, 160, 29, - 979, 998, 19, + 979, 998, 19, 22, 1019, 1006, 1022, 12, 6, 1017, 1017, 3, diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index 888f10a5e96b..cd33705a4320 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -48,8 +48,6 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe,16=s #define deb_getf(args...) dprintk(0x08,args) #define deb_stat(args...) dprintk(0x10,args) -static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr); - static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode, fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth) { @@ -463,10 +461,8 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe, int search_state,auto_val; u16 val; - if (tuner && state->config.pll_addr && state->config.pll_set) { /* initial call from dvb */ - dib3000mc_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe)); - state->config.pll_set(fe,fep,NULL); - dib3000mc_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe)); + if (tuner && state->config.pll_set) { /* initial call from dvb */ + state->config.pll_set(fe,fep); state->last_tuned_freq = fep->frequency; // if (!scanboost) { @@ -554,19 +550,15 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe, dib3000mc_set_adp_cfg(state,ofdm->constellation); wr_foreach(dib3000mc_reg_offset, dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]); - - } return 0; } static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode) { - struct dib3000_state *state; - + struct dib3000_state *state = fe->demodulator_priv; deb_info("init start\n"); - state = fe->demodulator_priv; state->timing_offset = 0; state->timing_offset_comp_done = 0; @@ -649,11 +641,9 @@ static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode) set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF); -/* if (state->config->pll_init) { - dib3000mc_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe)); - state->config->pll_init(fe,NULL); - dib3000mc_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe)); - }*/ + if (state->config.pll_init) + state->config.pll_init(fe); + deb_info("init end\n"); return 0; } @@ -688,7 +678,7 @@ static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) { struct dib3000_state* state = fe->demodulator_priv; - *unc = rd(DIB3000MC_REG_PACKET_ERROR_COUNT); + *unc = rd(DIB3000MC_REG_PACKET_ERRORS); return 0; } @@ -737,10 +727,7 @@ static int dib3000mc_sleep(struct dvb_frontend* fe) static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) { - tune->min_delay_ms = 2000; - tune->step_size = 166667; - tune->max_drift = 166667 * 2; - + tune->min_delay_ms = 1000; return 0; } diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 2a3c2ce7b2aa..f73b5f48e235 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -1,6 +1,4 @@ /* - * $Id: dvb-pll.c,v 1.7 2005/02/10 11:52:02 kraxel Exp $ - * * descriptions + helper functions for simple dvb plls. * * (c) 2004 Gerd Knorr [SuSE Labs] @@ -114,6 +112,92 @@ struct dvb_pll_desc dvb_pll_unknown_1 = { }; EXPORT_SYMBOL(dvb_pll_unknown_1); +/* Infineon TUA6010XS + * used in Thomson Cable Tuner + */ +struct dvb_pll_desc dvb_pll_tua6010xs = { + .name = "Infineon TUA6010XS", + .min = 44250000, + .max = 858000000, + .count = 3, + .entries = { + { 115750000, 36125000, 62500, 0x8e, 0x03 }, + { 403250000, 36125000, 62500, 0x8e, 0x06 }, + { 999999999, 36125000, 62500, 0x8e, 0x85 }, + }, +}; +EXPORT_SYMBOL(dvb_pll_tua6010xs); + +/* Panasonic env57h1xd5 (some Philips PLL ?) */ +struct dvb_pll_desc dvb_pll_env57h1xd5 = { + .name = "Panasonic ENV57H1XD5", + .min = 44250000, + .max = 858000000, + .count = 4, + .entries = { + { 153000000, 36291666, 166666, 0xc2, 0x41 }, + { 470000000, 36291666, 166666, 0xc2, 0x42 }, + { 526000000, 36291666, 166666, 0xc2, 0x84 }, + { 999999999, 36291666, 166666, 0xc2, 0xa4 }, + }, +}; +EXPORT_SYMBOL(dvb_pll_env57h1xd5); + +/* Philips TDA6650/TDA6651 + * used in Panasonic ENV77H11D5 + */ +static void tda665x_bw(u8 *buf, int bandwidth) +{ + if (bandwidth == BANDWIDTH_8_MHZ) + buf[3] |= 0x08; +} + +struct dvb_pll_desc dvb_pll_tda665x = { + .name = "Philips TDA6650/TDA6651", + .min = 44250000, + .max = 858000000, + .setbw = tda665x_bw, + .count = 12, + .entries = { + { 93834000, 36249333, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, + { 123834000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 161000000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 163834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 253834000, 36249333, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, + { 383834000, 36249333, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, + { 443834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 444000000, 36249333, 166667, 0xca, 0xc3 /* 110 0 0 0 11 */ }, + { 583834000, 36249333, 166667, 0xca, 0x63 /* 011 0 0 0 11 */ }, + { 793834000, 36249333, 166667, 0xca, 0xa3 /* 101 0 0 0 11 */ }, + { 444834000, 36249333, 166667, 0xca, 0xc3 /* 110 0 0 0 11 */ }, + { 861000000, 36249333, 166667, 0xca, 0xe3 /* 111 0 0 0 11 */ }, + } +}; +EXPORT_SYMBOL(dvb_pll_tda665x); + +/* Infineon TUA6034 + * used in LG TDTP E102P + */ +static void tua6034_bw(u8 *buf, int bandwidth) +{ + if (BANDWIDTH_7_MHZ != bandwidth) + buf[3] |= 0x08; +} + +struct dvb_pll_desc dvb_pll_tua6034 = { + .name = "Infineon TUA6034", + .min = 44250000, + .max = 858000000, + .count = 3, + .setbw = tua6034_bw, + .entries = { + { 174500000, 36166667, 62500, 0xce, 0x01 }, + { 230000000, 36166667, 62500, 0xce, 0x02 }, + { 999999999, 36166667, 62500, 0xce, 0x04 }, + }, +}; +EXPORT_SYMBOL(dvb_pll_tua6034); + /* ----------------------------------------------------------- */ /* code */ @@ -160,9 +244,3 @@ EXPORT_SYMBOL(dvb_pll_configure); MODULE_DESCRIPTION("dvb pll library"); MODULE_AUTHOR("Gerd Knorr"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index c4c3c56c4a81..b796778624b6 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -1,5 +1,5 @@ /* - * $Id: dvb-pll.h,v 1.2 2005/02/10 11:43:41 kraxel Exp $ + * descriptions + helper functions for simple dvb plls. */ #ifndef __DVB_PLL_H__ @@ -17,7 +17,7 @@ struct dvb_pll_desc { u32 stepsize; u8 cb1; u8 cb2; - } entries[9]; + } entries[12]; }; extern struct dvb_pll_desc dvb_pll_thomson_dtt7579; @@ -26,6 +26,11 @@ extern struct dvb_pll_desc dvb_pll_thomson_dtt7610; extern struct dvb_pll_desc dvb_pll_lg_z201; extern struct dvb_pll_desc dvb_pll_unknown_1; +extern struct dvb_pll_desc dvb_pll_tua6010xs; +extern struct dvb_pll_desc dvb_pll_env57h1xd5; +extern struct dvb_pll_desc dvb_pll_tua6034; +extern struct dvb_pll_desc dvb_pll_tda665x; + int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, u32 freq, int bandwidth); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 6c05fddb69ab..f9383e7f34ff 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -7,6 +7,19 @@ menu "Video For Linux" comment "Video Adapters" +config CONFIG_TUNER_MULTI_I2C + bool "Enable support for multiple I2C devices on Video Adapters (EXPERIMENTAL)" + depends on VIDEO_DEV && EXPERIMENTAL + ---help--- + Some video adapters have more than one tuner inside. This patch + enables support for using more than one tuner. This is required + for some cards to allow tunning both video and radio. + It also improves I2C autodetection for these cards. + + Only few tuners currently is supporting this. More to come. + + It is safe to say 'Y' here even if your card has only one I2C tuner. + config VIDEO_BT848 tristate "BT848 Video For Linux" depends on VIDEO_DEV && PCI && I2C @@ -330,6 +343,7 @@ config VIDEO_CX88_DVB select VIDEO_BUF_DVB select DVB_MT352 select DVB_OR51132 + select DVB_CX22702 ---help--- This adds support for DVB/ATSC cards based on the Connexant 2388x chip. diff --git a/drivers/media/video/bt832.c b/drivers/media/video/bt832.c index 07f72f64c5f7..9a642c7de545 100644 --- a/drivers/media/video/bt832.c +++ b/drivers/media/video/bt832.c @@ -6,7 +6,7 @@ It outputs an 8-bit 4:2:2 YUV or YCrCb video signal which can be directly connected to bt848/bt878 GPIO pins on this purpose. (see: VLSI Vision Ltd. www.vvl.co.uk for camera datasheets) - + Supported Cards: - Pixelview Rev.4E: 0x8a GPIO 0x400000 toggles Bt832 RESET, and the chip changes to i2c 0x88 ! @@ -31,8 +31,8 @@ #include #include -#include "id.h" -#include "audiochip.h" +#include +#include #include "bttv.h" #include "bt832.h" @@ -95,7 +95,7 @@ int bt832_init(struct i2c_client *i2c_client_s) buf=kmalloc(65,GFP_KERNEL); bt832_hexdump(i2c_client_s,buf); - + if(buf[0x40] != 0x31) { printk("bt832: this i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]); kfree(buf); @@ -135,7 +135,7 @@ int bt832_init(struct i2c_client *i2c_client_s) buf[1]= 0x27 & (~0x01); // Default | !skip if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) printk("bt832: i2c i/o error EO: rc == %d (should be 2)\n",rc); - + bt832_hexdump(i2c_client_s,buf); #if 0 @@ -168,8 +168,7 @@ int bt832_init(struct i2c_client *i2c_client_s) -static int bt832_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind) +static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) { struct bt832 *t; @@ -184,27 +183,32 @@ static int bt832_attach(struct i2c_adapter *adap, int addr, return -ENOMEM; memset(t,0,sizeof(*t)); t->client = client_template; - t->client.data = t; + i2c_set_clientdata(&t->client, t); i2c_attach_client(&t->client); if(! bt832_init(&t->client)) { bt832_detach(&t->client); return -1; } - + return 0; } static int bt832_probe(struct i2c_adapter *adap) { +#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, bt832_attach); +#else + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, bt832_attach); +#endif return 0; } static int bt832_detach(struct i2c_client *client) { - struct bt832 *t = (struct bt832*)client->data; + struct bt832 *t = i2c_get_clientdata(client); printk("bt832: detach.\n"); i2c_detach_client(client); @@ -215,7 +219,7 @@ static int bt832_detach(struct i2c_client *client) static int bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct bt832 *t = (struct bt832*)client->data; + struct bt832 *t = i2c_get_clientdata(client); printk("bt832: command %x\n",cmd); @@ -249,19 +253,18 @@ static struct i2c_driver driver = { }; static struct i2c_client client_template = { - .name = "bt832", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + I2C_DEVNAME("bt832"), + .flags = I2C_CLIENT_ALLOW_USE, + .driver = &driver, }; -int bt832_init_module(void) +static int __init bt832_init_module(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void bt832_cleanup_module(void) +static void __exit bt832_cleanup_module(void) { i2c_del_driver(&driver); } @@ -269,3 +272,10 @@ static void bt832_cleanup_module(void) module_init(bt832_init_module); module_exit(bt832_cleanup_module); +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/bt832.h b/drivers/media/video/bt832.h index 7a98c06e0e34..9b6a8d2c96b5 100644 --- a/drivers/media/video/bt832.h +++ b/drivers/media/video/bt832.h @@ -1,6 +1,6 @@ /* Bt832 CMOS Camera Video Processor (VP) - The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS + The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS color digital camera directly to video capture devices via an 8-bit, 4:2:2 YUV or YCrCb video interface. @@ -85,7 +85,7 @@ #define BT832_DEVICE_ID 63 # define BT832_DEVICE_ID__31 0x31 // Bt832 has ID 0x31 -/* STMicroelectronivcs VV5404 camera module +/* STMicroelectronivcs VV5404 camera module i2c: 0x20: sensor address i2c: 0xa0: eeprom for ccd defect map */ @@ -256,26 +256,26 @@ For the CCIR-601 standards, the sampling is based on a static orthogonal samplin //=========================================================================== // Timing generator SRAM table values for CCIR601 720x480 NTSC //=========================================================================== -// For NTSC CCIR656 +// For NTSC CCIR656 BYTE BtCard::SRAMTable_NTSC[] = { // SRAM Timing Table for NTSC - 0x0c, 0xc0, 0x00, - 0x00, 0x90, 0xc2, - 0x03, 0x10, 0x03, - 0x06, 0x10, 0x34, - 0x12, 0x12, 0x65, - 0x02, 0x13, 0x24, - 0x19, 0x00, 0x24, - 0x39, 0x00, 0x96, - 0x59, 0x08, 0x93, + 0x0c, 0xc0, 0x00, + 0x00, 0x90, 0xc2, + 0x03, 0x10, 0x03, + 0x06, 0x10, 0x34, + 0x12, 0x12, 0x65, + 0x02, 0x13, 0x24, + 0x19, 0x00, 0x24, + 0x39, 0x00, 0x96, + 0x59, 0x08, 0x93, 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, - 0xc0, 0x40, 0x30, - 0x86, 0x01, 0x01, - 0xa6, 0x0d, 0x62, - 0x03, 0x11, 0x61, - 0x05, 0x37, 0x30, + 0x03, 0x50, 0x30, + 0xc0, 0x40, 0x30, + 0x86, 0x01, 0x01, + 0xa6, 0x0d, 0x62, + 0x03, 0x11, 0x61, + 0x05, 0x37, 0x30, 0xac, 0x21, 0x50 }; diff --git a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c index 6334122704ae..251092e7f19f 100644 --- a/drivers/media/video/bttv-cards.c +++ b/drivers/media/video/bttv-cards.c @@ -1,5 +1,5 @@ /* - $Id: bttv-cards.c,v 1.47 2005/02/22 14:06:32 kraxel Exp $ + $Id: bttv-cards.c,v 1.49 2005/06/10 17:20:24 mchehab Exp $ bttv-cards.c @@ -51,6 +51,7 @@ static void avermedia_eeprom(struct bttv *btv); static void osprey_eeprom(struct bttv *btv); static void modtec_eeprom(struct bttv *btv); static void init_PXC200(struct bttv *btv); +static void init_RTV24(struct bttv *btv); static void winview_audio(struct bttv *btv, struct video_audio *v, int set); static void lt9415_audio(struct bttv *btv, struct video_audio *v, int set); @@ -2251,6 +2252,20 @@ struct tvcard bttv_tvcards[] = { .no_tda7432 = 1, .no_tda9875 = 1, .muxsel_hook = kodicom4400r_muxsel, +}, +{ + /* ---- card 0x85---------------------------------- */ + /* Michael Henson */ + /* Adlink RTV24 with special unlock codes */ + .name = "Adlink RTV24", + .video_inputs = 4, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .muxsel = { 2, 3, 1, 0}, + .tuner_type = -1, + .pll = PLL_28, + }}; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -2636,6 +2651,10 @@ void __devinit bttv_init_card1(struct bttv *btv) case BTTV_AVDVBT_771: btv->use_i2c_hw = 1; break; + case BTTV_ADLINK_RTV24: + init_RTV24( btv ); + break; + } if (!bttv_tvcards[btv->c.type].has_dvb) bttv_reset_audio(btv); @@ -2784,6 +2803,8 @@ void __devinit bttv_init_card2(struct bttv *btv) } btv->pll.pll_current = -1; + bttv_reset_audio(btv); + /* tuner configuration (from card list / autodetect / insmod option) */ if (UNSET != bttv_tvcards[btv->c.type].tuner_type) if(UNSET == btv->tuner_type) @@ -3304,6 +3325,83 @@ static void __devinit init_PXC200(struct bttv *btv) } + +/* ----------------------------------------------------------------------- */ +/* + * The Adlink RTV-24 (aka Angelo) has some special initialisation to unlock + * it. This apparently involves the following procedure for each 878 chip: + * + * 1) write 0x00C3FEFF to the GPIO_OUT_EN register + * + * 2) write to GPIO_DATA + * - 0x0E + * - sleep 1ms + * - 0x10 + 0x0E + * - sleep 10ms + * - 0x0E + * read from GPIO_DATA into buf (uint_32) + * - if ( data>>18 & 0x01 != 0) || ( buf>>19 & 0x01 != 1 ) + * error. ERROR_CPLD_Check_Failed stop. + * + * 3) write to GPIO_DATA + * - write 0x4400 + 0x0E + * - sleep 10ms + * - write 0x4410 + 0x0E + * - sleep 1ms + * - write 0x0E + * read from GPIO_DATA into buf (uint_32) + * - if ( buf>>18 & 0x01 ) || ( buf>>19 && 0x01 != 0 ) + * error. ERROR_CPLD_Check_Failed. + */ +/* ----------------------------------------------------------------------- */ +void +init_RTV24 (struct bttv *btv) +{ + uint32_t dataRead = 0; + long watchdog_value = 0x0E; + + printk (KERN_INFO + "bttv%d: Adlink RTV-24 initialisation in progress ...\n", + btv->c.nr); + + btwrite (0x00c3feff, BT848_GPIO_OUT_EN); + + btwrite (0 + watchdog_value, BT848_GPIO_DATA); + msleep (1); + btwrite (0x10 + watchdog_value, BT848_GPIO_DATA); + msleep (10); + btwrite (0 + watchdog_value, BT848_GPIO_DATA); + + dataRead = btread (BT848_GPIO_DATA); + + if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) { + printk (KERN_INFO + "bttv%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n", + btv->c.nr, dataRead); + } + + btwrite (0x4400 + watchdog_value, BT848_GPIO_DATA); + msleep (10); + btwrite (0x4410 + watchdog_value, BT848_GPIO_DATA); + msleep (1); + btwrite (watchdog_value, BT848_GPIO_DATA); + msleep (1); + dataRead = btread (BT848_GPIO_DATA); + + if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) { + printk (KERN_INFO + "bttv%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n", + btv->c.nr, dataRead); + + return; + } + + printk (KERN_INFO + "bttv%d: Adlink RTV-24 initialisation complete.\n", btv->c.nr); +} + + + /* ----------------------------------------------------------------------- */ /* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */ /* diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index 033cc5498f23..290289a99757 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -1,5 +1,5 @@ /* - $Id: bttv-driver.c,v 1.37 2005/02/21 13:57:59 kraxel Exp $ + $Id: bttv-driver.c,v 1.38 2005/06/10 17:20:24 mchehab Exp $ bttv - Bt848 frame grabber driver diff --git a/drivers/media/video/bttv-i2c.c b/drivers/media/video/bttv-i2c.c index c2368bc832ed..da448a5f9e9c 100644 --- a/drivers/media/video/bttv-i2c.c +++ b/drivers/media/video/bttv-i2c.c @@ -1,5 +1,5 @@ /* - $Id: bttv-i2c.c,v 1.18 2005/02/16 12:14:10 kraxel Exp $ + $Id: bttv-i2c.c,v 1.21 2005/06/10 17:20:24 mchehab Exp $ bttv-i2c.c -- all the i2c code is here diff --git a/drivers/media/video/bttv.h b/drivers/media/video/bttv.h index 8322b66e0905..191eaf1714ba 100644 --- a/drivers/media/video/bttv.h +++ b/drivers/media/video/bttv.h @@ -1,5 +1,5 @@ /* - * $Id: bttv.h,v 1.17 2005/02/22 14:06:32 kraxel Exp $ + * $Id: bttv.h,v 1.18 2005/05/24 23:41:42 nsh Exp $ * * bttv - Bt848 frame grabber driver * @@ -135,6 +135,7 @@ #define BTTV_DVICO_DVBT_LITE 0x80 #define BTTV_TIBET_CS16 0x83 #define BTTV_KODICOM_4400R 0x84 +#define BTTV_ADLINK_RTV24 0x85 /* i2c address list */ #define I2C_TSA5522 0xc2 diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 1a9ba7e1cf51..7b6f1e856028 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -226,10 +226,6 @@ extern int fini_bttv_i2c(struct bttv *btv); #define dprintk if (bttv_debug >= 1) printk #define d2printk if (bttv_debug >= 2) printk -/* our devices */ -#define BTTV_MAX 16 -extern unsigned int bttv_num; - #define BTTV_MAX_FBUF 0x208000 #define VBIBUF_SIZE (2048*VBI_MAXLINES*2) #define BTTV_TIMEOUT (HZ/2) /* 0.5 seconds */ @@ -375,6 +371,10 @@ struct bttv { unsigned int users; struct bttv_fh init; }; + +/* our devices */ +#define BTTV_MAX 16 +extern unsigned int bttv_num; extern struct bttv bttvs[BTTV_MAX]; /* private ioctls */ diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 46d6778b863b..91f8afeded88 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-blackbird.c,v 1.26 2005/03/07 15:58:05 kraxel Exp $ + * $Id: cx88-blackbird.c,v 1.27 2005/06/03 13:31:50 mchehab Exp $ * * Support for a cx23416 mpeg encoder via cx2388x host port. * "blackbird" reference design. @@ -61,37 +61,304 @@ static LIST_HEAD(cx8802_devlist); #define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF -/*Firmware API commands*/ -#define IVTV_API_ENC_PING_FW 0x00000080 -#define IVTV_API_ENC_GETVER 0x000000C4 -#define IVTV_API_ENC_HALT_FW 0x000000C3 -#define IVTV_API_STD_TIMEOUT 0x00010000 /*units??*/ -//#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0x000000c7 -#define IVTV_API_ASSIGN_STREAM_TYPE 0x000000b9 -#define IVTV_API_ASSIGN_OUTPUT_PORT 0x000000bb -#define IVTV_API_ASSIGN_FRAMERATE 0x0000008f -#define IVTV_API_ASSIGN_FRAME_SIZE 0x00000091 -#define IVTV_API_ASSIGN_ASPECT_RATIO 0x00000099 -#define IVTV_API_ASSIGN_BITRATES 0x00000095 -#define IVTV_API_ASSIGN_GOP_PROPERTIES 0x00000097 -#define IVTV_API_ASSIGN_3_2_PULLDOWN 0x000000b1 -#define IVTV_API_ASSIGN_GOP_CLOSURE 0x000000c5 -#define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0x000000bd -#define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x0000009b -#define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x0000009d -#define IVTV_API_ASSIGN_CORING_LEVELS 0x0000009f -#define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0x000000a1 -#define IVTV_API_ASSIGN_FRAME_DROP_RATE 0x000000d0 -#define IVTV_API_ASSIGN_PLACEHOLDER 0x000000d8 -#define IVTV_API_MUTE_VIDEO 0x000000d9 -#define IVTV_API_MUTE_AUDIO 0x000000da -#define IVTV_API_INITIALIZE_INPUT 0x000000cd -#define IVTV_API_REFRESH_INPUT 0x000000d3 -#define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0x000000d6 -#define IVTV_API_BEGIN_CAPTURE 0x00000081 -//#define IVTV_API_PAUSE_ENCODER 0x000000d2 -//#define IVTV_API_EVENT_NOTIFICATION 0x000000d5 -#define IVTV_API_END_CAPTURE 0x00000082 +/* Firmware API commands */ +/* #define IVTV_API_STD_TIMEOUT 0x00010000 // 65536, units?? */ +#define IVTV_API_STD_TIMEOUT 500 + +#define BLACKBIRD_API_PING 0x80 +#define BLACKBIRD_API_BEGIN_CAPTURE 0x81 +enum blackbird_capture_type { + BLACKBIRD_MPEG_CAPTURE, + BLACKBIRD_RAW_CAPTURE, + BLACKBIRD_RAW_PASSTHRU_CAPTURE +}; +enum blackbird_capture_bits { + BLACKBIRD_RAW_BITS_NONE = 0x00, + BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01, + BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02, + BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04, + BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08, + BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10 +}; +#define BLACKBIRD_API_END_CAPTURE 0x82 +enum blackbird_capture_end { + BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */ + BLACKBIRD_END_NOW, /* stop immediately, no irq */ +}; +#define BLACKBIRD_API_SET_AUDIO_ID 0x89 +#define BLACKBIRD_API_SET_VIDEO_ID 0x8B +#define BLACKBIRD_API_SET_PCR_ID 0x8D +#define BLACKBIRD_API_SET_FRAMERATE 0x8F +enum blackbird_framerate { + BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */ + BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */ +}; +#define BLACKBIRD_API_SET_RESOLUTION 0x91 +#define BLACKBIRD_API_SET_VIDEO_BITRATE 0x95 +enum blackbird_video_bitrate_type { + BLACKBIRD_VIDEO_VBR, + BLACKBIRD_VIDEO_CBR +}; +#define BLACKBIRD_PEAK_RATE_DIVISOR 400 +enum blackbird_mux_rate { + BLACKBIRD_MUX_RATE_DEFAULT, + /* dvd mux rate: multiply by 400 to get the actual rate */ + BLACKBIRD_MUX_RATE_DVD = 25200 +}; +#define BLACKBIRD_API_SET_GOP_STRUCTURE 0x97 +#define BLACKBIRD_API_SET_ASPECT_RATIO 0x99 +enum blackbird_aspect_ratio { + BLACKBIRD_ASPECT_RATIO_FORBIDDEN, + BLACKBIRD_ASPECT_RATIO_1_1_SQUARE, + BLACKBIRD_ASPECT_RATIO_4_3, + BLACKBIRD_ASPECT_RATIO_16_9, + BLACKBIRD_ASPECT_RATIO_221_100, + BLACKBIRD_ASPECT_RATIO_RESERVED +}; +#define BLACKBIRD_API_SET_DNR_MODE 0x9B +enum blackbird_dnr_bits { + BLACKBIRD_DNR_BITS_MANUAL, + BLACKBIRD_DNR_BITS_AUTO_SPATIAL, + BLACKBIRD_DNR_BITS_AUTO_TEMPORAL, + BLACKBIRD_DNR_BITS_AUTO +}; +enum blackbird_median_filter { + BLACKBIRD_MEDIAN_FILTER_DISABLED, + BLACKBIRD_MEDIAN_FILTER_HORIZONTAL, + BLACKBIRD_MEDIAN_FILTER_VERTICAL, + BLACKBIRD_MEDIAN_FILTER_HV, + BLACKBIRD_MEDIAN_FILTER_DIAGONAL +}; +#define BLACKBIRD_API_SET_MANUAL_DNR 0x9D +#define BLACKBIRD_API_SET_DNR_MEDIAN 0x9F +#define BLACKBIRD_API_SET_SPATIAL_FILTER 0xA1 +enum blackbird_spatial_filter_luma { + BLACKBIRD_SPATIAL_FILTER_LUMA_DISABLED, + BLACKBIRD_SPATIAL_FILTER_LUMA_1D_HORIZ, + BLACKBIRD_SPATIAL_FILTER_LUMA_1D_VERT, + BLACKBIRD_SPATIAL_FILTER_LUMA_2D_HV, /* separable, default */ + BLACKBIRD_SPATIAL_FILTER_LUMA_2D_SYMM /* symmetric non-separable */ +}; +enum blackbird_spatial_filter_chroma { + BLACKBIRD_SPATIAL_FILTER_CHROMA_DISABLED, + BLACKBIRD_SPATIAL_FILTER_CHROMA_1D_HORIZ /* default */ +}; +#define BLACKBIRD_API_SET_3_2_PULLDOWN 0xB1 +enum blackbird_pulldown { + BLACKBIRD_3_2_PULLDOWN_DISABLED, + BLACKBIRD_3_2_PULLDOWN_ENABLED +}; +#define BLACKBIRD_API_SET_VBI_LINE_NO 0xB7 +enum blackbird_vbi_line_bits { + BLACKBIRD_VBI_LINE_BITS_TOP_FIELD, + BLACKBIRD_VBI_LINE_BITS_BOT_FIELD = (1 << 31), + BLACKBIRD_VBI_LINE_BITS_ALL_LINES = 0xFFFFFFFF +}; +enum blackbird_vbi_line { + BLACKBIRD_VBI_LINE_DISABLED, + BLACKBIRD_VBI_LINE_ENABLED +}; +enum blackbird_vbi_slicing { + BLACKBIRD_VBI_SLICING_NONE, + BLACKBIRD_VBI_SLICING_CLOSED_CAPTION +}; +#define BLACKBIRD_API_SET_STREAM_TYPE 0xB9 +enum blackbird_stream_type { + BLACKBIRD_STREAM_PROGRAM, + BLACKBIRD_STREAM_TRANSPORT, + BLACKBIRD_STREAM_MPEG1, + BLACKBIRD_STREAM_PES_AV, + BLACKBIRD_STREAM_UNKNOWN4, + BLACKBIRD_STREAM_PES_VIDEO, + BLACKBIRD_STREAM_UNKNOWN6, + BLACKBIRD_STREAM_PES_AUDIO, + BLACKBIRD_STREAM_UNKNOWN8, + BLACKBIRD_STREAM_UNKNOWN9, /* audio/pcm ? */ + BLACKBIRD_STREAM_DVD, + BLACKBIRD_STREAM_VCD, + BLACKBIRD_STREAM_UNKNOWN12 /* svcd/xvcd ? */ +}; +#define BLACKBIRD_API_SET_OUTPUT_PORT 0xBB +enum blackbird_stream_port { + BLACKBIRD_OUTPUT_PORT_MEMORY, + BLACKBIRD_OUTPUT_PORT_STREAMING, + BLACKBIRD_OUTPUT_PORT_SERIAL +}; +#define BLACKBIRD_API_SET_AUDIO_PARAMS 0xBD +enum blackbird_audio_bits_sample_rate { + BLACKBIRD_AUDIO_BITS_44100HZ, + BLACKBIRD_AUDIO_BITS_48000HZ, + BLACKBIRD_AUDIO_BITS_32000HZ, + BLACKBIRD_AUDIO_BITS_RESERVED_HZ, +}; +enum blackbird_audio_bits_encoding { + BLACKBIRD_AUDIO_BITS_LAYER_1 = 0x1 << 2, + BLACKBIRD_AUDIO_BITS_LAYER_2 = 0x2 << 2, +}; +enum blackbird_audio_bits_bitrate_layer_1 { + BLACKBIRD_AUDIO_BITS_LAYER_1_FREE_FORMAT, + BLACKBIRD_AUDIO_BITS_LAYER_1_32 = 0x01 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_64 = 0x02 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_96 = 0x03 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_128 = 0x04 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_160 = 0x05 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_192 = 0x06 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_224 = 0x07 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_256 = 0x08 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_288 = 0x09 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_320 = 0x0A << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_352 = 0x0B << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_384 = 0x0C << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_416 = 0x0D << 4, + BLACKBIRD_AUDIO_BITS_LAYER_1_448 = 0x0E << 4, +}; +enum blackbird_audio_bits_bitrate_layer_2 { + BLACKBIRD_AUDIO_BITS_LAYER_2_FREE_FORMAT, + BLACKBIRD_AUDIO_BITS_LAYER_2_32 = 0x01 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_48 = 0x02 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_56 = 0x03 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_64 = 0x04 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_80 = 0x05 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_96 = 0x06 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_112 = 0x07 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_128 = 0x08 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_160 = 0x09 << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_192 = 0x0A << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_224 = 0x0B << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_256 = 0x0C << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_320 = 0x0D << 4, + BLACKBIRD_AUDIO_BITS_LAYER_2_384 = 0x0E << 4, +}; +enum blackbird_audio_bits_mode { + BLACKBIRD_AUDIO_BITS_STEREO, + BLACKBIRD_AUDIO_BITS_JOINT_STEREO = 0x1 << 8, + BLACKBIRD_AUDIO_BITS_DUAL = 0x2 << 8, + BLACKBIRD_AUDIO_BITS_MONO = 0x3 << 8, +}; +enum blackbird_audio_bits_mode_extension { + BLACKBIRD_AUDIO_BITS_BOUND_4, + BLACKBIRD_AUDIO_BITS_BOUND_8 = 0x1 << 10, + BLACKBIRD_AUDIO_BITS_BOUND_12 = 0x2 << 10, + BLACKBIRD_AUDIO_BITS_BOUND_16 = 0x3 << 10, +}; +enum blackbird_audio_bits_emphasis { + BLACKBIRD_AUDIO_BITS_EMPHASIS_NONE, + BLACKBIRD_AUDIO_BITS_EMPHASIS_50_15 = 0x1 << 12, + BLACKBIRD_AUDIO_BITS_EMPHASIS_RESERVED = 0x2 << 12, + BLACKBIRD_AUDIO_BITS_EMPHASIS_CCITT_J17 = 0x3 << 12, +}; +enum blackbird_audio_bits_crc { + BLACKBIRD_AUDIO_BITS_CRC_OFF, + BLACKBIRD_AUDIO_BITS_CRC_ON = 0x1 << 14, +}; +enum blackbird_audio_bits_copyright { + BLACKBIRD_AUDIO_BITS_COPYRIGHT_OFF, + BLACKBIRD_AUDIO_BITS_COPYRIGHT_ON = 0x1 << 15, +}; +enum blackbird_audio_bits_original { + BLACKBIRD_AUDIO_BITS_COPY, + BLACKBIRD_AUDIO_BITS_ORIGINAL = 0x1 << 16, +}; +#define BLACKBIRD_API_HALT 0xC3 +#define BLACKBIRD_API_GET_VERSION 0xC4 +#define BLACKBIRD_API_SET_GOP_CLOSURE 0xC5 +enum blackbird_gop_closure { + BLACKBIRD_GOP_CLOSURE_OFF, + BLACKBIRD_GOP_CLOSURE_ON, +}; +#define BLACKBIRD_API_DATA_XFER_STATUS 0xC6 +enum blackbird_data_xfer_status { + BLACKBIRD_MORE_BUFFERS_FOLLOW, + BLACKBIRD_LAST_BUFFER, +}; +#define BLACKBIRD_API_PROGRAM_INDEX_INFO 0xC7 +enum blackbird_picture_mask { + BLACKBIRD_PICTURE_MASK_NONE, + BLACKBIRD_PICTURE_MASK_I_FRAMES, + BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3, + BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7, +}; +#define BLACKBIRD_API_SET_VBI_PARAMS 0xC8 +enum blackbird_vbi_mode_bits { + BLACKBIRD_VBI_BITS_SLICED, + BLACKBIRD_VBI_BITS_RAW, +}; +enum blackbird_vbi_insertion_bits { + BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, + BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, + BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, + BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, + BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, +}; +#define BLACKBIRD_API_SET_DMA_BLOCK_SIZE 0xC9 +enum blackbird_dma_unit { + BLACKBIRD_DMA_BYTES, + BLACKBIRD_DMA_FRAMES, +}; +#define BLACKBIRD_API_DMA_TRANSFER_INFO 0xCA +#define BLACKBIRD_API_DMA_TRANSFER_STAT 0xCB +enum blackbird_dma_transfer_status_bits { + BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01, + BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04, + BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10, +}; +#define BLACKBIRD_API_SET_DMA2HOST_ADDR 0xCC +#define BLACKBIRD_API_INIT_VIDEO_INPUT 0xCD +#define BLACKBIRD_API_SET_FRAMESKIP 0xD0 +#define BLACKBIRD_API_PAUSE 0xD2 +enum blackbird_pause { + BLACKBIRD_PAUSE_ENCODING, + BLACKBIRD_RESUME_ENCODING, +}; +#define BLACKBIRD_API_REFRESH_INPUT 0xD3 +#define BLACKBIRD_API_SET_COPYRIGHT 0xD4 +enum blackbird_copyright { + BLACKBIRD_COPYRIGHT_OFF, + BLACKBIRD_COPYRIGHT_ON, +}; +#define BLACKBIRD_API_SET_NOTIFICATION 0xD5 +enum blackbird_notification_type { + BLACKBIRD_NOTIFICATION_REFRESH, +}; +enum blackbird_notification_status { + BLACKBIRD_NOTIFICATION_OFF, + BLACKBIRD_NOTIFICATION_ON, +}; +enum blackbird_notification_mailbox { + BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1, +}; +#define BLACKBIRD_API_SET_CAPTURE_LINES 0xD6 +enum blackbird_field1_lines { + BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */ + BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */ + BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */ +}; +enum blackbird_field2_lines { + BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */ + BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */ + BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */ +}; +#define BLACKBIRD_API_SET_CUSTOM_DATA 0xD7 +enum blackbird_custom_data_type { + BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, + BLACKBIRD_CUSTOM_PRIVATE_PACKET, +}; +#define BLACKBIRD_API_MUTE_VIDEO 0xD9 +enum blackbird_mute { + BLACKBIRD_UNMUTE, + BLACKBIRD_MUTE, +}; +enum blackbird_mute_video_mask { + BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00, + BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000, + BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000, +}; +enum blackbird_mute_video_shift { + BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8, + BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16, + BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24, +}; +#define BLACKBIRD_API_MUTE_AUDIO 0xDA /* Registers */ #define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) @@ -405,68 +672,100 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) return 0; } +/** + Settings used by the windows tv app for PVR2000: +================================================================================================================= +Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode +----------------------------------------------------------------------------------------------------------------- +MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo +MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo +VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo +DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo +DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo +================================================================================================================= +*DB: "DirectBurn" +*/ static void blackbird_codec_settings(struct cx8802_dev *dev) { int bitrate_mode = 1; int bitrate = 7500000; int bitrate_peak = 7500000; +#if 1 + bitrate_mode = BLACKBIRD_VIDEO_CBR; + bitrate = 4000*1024; + bitrate_peak = 4000*1024; +#endif /* assign stream type */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 0); /* program stream */ - //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 2); /* MPEG1 stream */ - //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 3); /* PES A/V */ - //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 10); /* DVD stream */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_STREAM_TYPE, 1, 0, BLACKBIRD_STREAM_PROGRAM); + /* blackbird_api_cmd(dev, BLACKBIRD_API_SET_STREAM_TYPE, 1, 0, BLACKBIRD_STREAM_TRANSPORT); */ - /* assign output port */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_OUTPUT_PORT, 1, 0, 1); /* 1 = Host */ + /* assign output port */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_OUTPUT_PORT, 1, 0, BLACKBIRD_OUTPUT_PORT_STREAMING); /* Host */ - /* assign framerate */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAMERATE, 1, 0, 0); + /* assign framerate */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_FRAMERATE, 1, 0, BLACKBIRD_FRAMERATE_PAL_25); - /* assign frame size */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_SIZE, 2, 0, + /* assign frame size */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_RESOLUTION, 2, 0, dev->height, dev->width); - /* assign aspect ratio */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_ASPECT_RATIO, 1, 0, 2); + /* assign aspect ratio */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_ASPECT_RATIO, 1, 0, BLACKBIRD_ASPECT_RATIO_4_3); - /* assign bitrates */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_BITRATES, 5, 0, + /* assign bitrates */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_VIDEO_BITRATE, 5, 0, bitrate_mode, /* mode */ bitrate, /* bps */ - bitrate_peak / 400, /* peak/400 */ - 0, 0x70); /* encoding buffer, ckennedy */ + bitrate_peak / BLACKBIRD_PEAK_RATE_DIVISOR, /* peak/400 */ + BLACKBIRD_MUX_RATE_DEFAULT /*, 0x70*/); /* encoding buffer, ckennedy */ - /* assign gop properties */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 15, 3); - //blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 2, 1); + /* assign gop properties */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_GOP_STRUCTURE, 2, 0, 15, 3); - /* assign 3 2 pulldown */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_3_2_PULLDOWN, 1, 0, 0); + /* assign 3 2 pulldown */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_3_2_PULLDOWN, 1, 0, BLACKBIRD_3_2_PULLDOWN_DISABLED); - /* note: it's not necessary to set the samplerate, the mpeg encoder seems to autodetect/adjust */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, (2<<2) | (8<<4)); + /* assign audio properties */ + /* note: it's not necessary to set the samplerate, the mpeg encoder seems to autodetect/adjust */ + /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, (2<<2) | (8<<4)); + blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, 0 | (2 << 2) | (14 << 4)); */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_AUDIO_PARAMS, 1, 0, + BLACKBIRD_AUDIO_BITS_44100HZ | + BLACKBIRD_AUDIO_BITS_LAYER_2 | + BLACKBIRD_AUDIO_BITS_LAYER_2_224 | + BLACKBIRD_AUDIO_BITS_STEREO | + /* BLACKBIRD_AUDIO_BITS_BOUND_4 | */ + BLACKBIRD_AUDIO_BITS_EMPHASIS_NONE | + BLACKBIRD_AUDIO_BITS_CRC_OFF | + BLACKBIRD_AUDIO_BITS_COPYRIGHT_OFF | + BLACKBIRD_AUDIO_BITS_COPY + ); /* assign gop closure */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_CLOSURE, 1, 0, 0); + blackbird_api_cmd(dev, BLACKBIRD_API_SET_GOP_CLOSURE, 1, 0, BLACKBIRD_GOP_CLOSURE_OFF); - /* assign audio properties */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, 0 | (2 << 2) | (14 << 4)); - /* assign dnr filter mode */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_MODE, 2, 0, 0, 0); + /* assign dnr filter mode */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_DNR_MODE, 2, 0, + BLACKBIRD_DNR_BITS_MANUAL, + BLACKBIRD_MEDIAN_FILTER_DISABLED + ); - /* assign dnr filter props*/ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_PROPS, 2, 0, 0, 0); + /* assign dnr filter props*/ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_MANUAL_DNR, 2, 0, 0, 0); - /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_CORING_LEVELS, 4, 0, 0, 255, 0, 255); + /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_DNR_MEDIAN, 4, 0, 0, 255, 0, 255); - /* assign spatial filter type: luma_t: 1 = horiz_only, chroma_t: 1 = horiz_only */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, 2, 0, 1, 1); + /* assign spatial filter type: luma_t: horiz_only, chroma_t: horiz_only */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_SPATIAL_FILTER, 2, 0, + BLACKBIRD_SPATIAL_FILTER_LUMA_1D_HORIZ, + BLACKBIRD_SPATIAL_FILTER_CHROMA_1D_HORIZ + ); - /* assign frame drop rate */ - blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_DROP_RATE, 1, 0, 0); + /* assign frame drop rate */ + /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_DROP_RATE, 1, 0, 0); */ } static int blackbird_initialize_codec(struct cx8802_dev *dev) @@ -476,7 +775,7 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) int retval; dprintk(1,"Initialize codec\n"); - retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */ + retval = blackbird_api_cmd(dev, BLACKBIRD_API_PING, 0, 0); /* ping */ if (retval < 0) { /* ping was not successful, reset and upload firmware */ cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ @@ -491,13 +790,13 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) if (dev->mailbox < 0) return -1; - retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */ + retval = blackbird_api_cmd(dev, BLACKBIRD_API_PING, 0, 0); /* ping */ if (retval < 0) { dprintk(0, "ERROR: Firmware ping failed!\n"); return -1; } - retval = blackbird_api_cmd(dev, IVTV_API_ENC_GETVER, 0, 1, &version); + retval = blackbird_api_cmd(dev, BLACKBIRD_API_GET_VERSION, 0, 1, &version); if (retval < 0) { dprintk(0, "ERROR: Firmware get encoder version failed!\n"); return -1; @@ -517,25 +816,36 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) blackbird_codec_settings(dev); msleep(1); - //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef); - blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0); - //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); - blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef); + blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0); + blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_CAPTURE_LINES, 2, 0, + BLACKBIRD_FIELD1_SAA7115, + BLACKBIRD_FIELD1_SAA7115 + ); - blackbird_api_cmd(dev, IVTV_API_INITIALIZE_INPUT, 0, 0); /* initialize the video input */ + /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */ + blackbird_api_cmd(dev, BLACKBIRD_API_SET_CUSTOM_DATA, 12, 0, + BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + blackbird_api_cmd(dev, BLACKBIRD_API_INIT_VIDEO_INPUT, 0, 0); /* initialize the video input */ msleep(1); - blackbird_api_cmd(dev, IVTV_API_MUTE_VIDEO, 1, 0, 0); + blackbird_api_cmd(dev, BLACKBIRD_API_MUTE_VIDEO, 1, 0, BLACKBIRD_UNMUTE); msleep(1); - blackbird_api_cmd(dev, IVTV_API_MUTE_AUDIO, 1, 0, 0); + blackbird_api_cmd(dev, BLACKBIRD_API_MUTE_AUDIO, 1, 0, BLACKBIRD_UNMUTE); msleep(1); - blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); /* start capturing to the host interface */ - //blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0); /* start capturing to the host interface */ - msleep(1); + /* blackbird_api_cmd(dev, BLACKBIRD_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); // start capturing to the host interface */ + blackbird_api_cmd(dev, BLACKBIRD_API_BEGIN_CAPTURE, 2, 0, + BLACKBIRD_MPEG_CAPTURE, + BLACKBIRD_RAW_BITS_NONE + ); /* start capturing to the host interface */ + msleep(10); - blackbird_api_cmd(dev, IVTV_API_REFRESH_INPUT, 0,0); + blackbird_api_cmd(dev, BLACKBIRD_API_REFRESH_INPUT, 0,0); return 0; } @@ -709,7 +1019,12 @@ static int mpeg_release(struct inode *inode, struct file *file) { struct cx8802_fh *fh = file->private_data; - blackbird_api_cmd(fh->dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13); + /* blackbird_api_cmd(fh->dev, BLACKBIRD_API_END_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */ + blackbird_api_cmd(fh->dev, BLACKBIRD_API_END_CAPTURE, 3, 0, + BLACKBIRD_END_NOW, + BLACKBIRD_MPEG_CAPTURE, + BLACKBIRD_RAW_BITS_NONE + ); /* stop mpeg capture */ if (fh->mpegq.streaming) @@ -908,4 +1223,5 @@ module_exit(blackbird_fini); * Local variables: * c-basic-offset: 8 * End: + * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off */ diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 367624822d77..b3fb04356b71 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-cards.c,v 1.66 2005/03/04 09:12:23 kraxel Exp $ + * $Id: cx88-cards.c,v 1.76 2005/06/08 01:28:09 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * card-specific stuff. @@ -35,6 +35,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_UNKNOWN] = { .name = "UNKNOWN/GENERIC", .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 0, @@ -52,6 +55,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_HAUPPAUGE] = { .name = "Hauppauge WinTV 34xxx models", .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -78,6 +84,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_GDI] = { .name = "GDI Black Gold", .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -85,7 +94,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_PIXELVIEW] = { .name = "PixelView", - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -104,7 +116,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_ATI_WONDER_PRO] = { .name = "ATI TV Wonder Pro", - .tuner_type = 44, + .tuner_type = TUNER_PHILIPS_4IN1, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -122,7 +137,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_WINFAST2000XP_EXPERT] = { .name = "Leadtek Winfast 2000XP Expert", - .tuner_type = 44, + .tuner_type = TUNER_PHILIPS_4IN1, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -156,7 +174,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_AVERTV_303] = { .name = "AverTV Studio 303 (M126)", - .tuner_type = 38, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -179,7 +200,10 @@ struct cx88_board cx88_boards[] = { // added gpio values thanks to Michal // values for PAL from DScaler .name = "MSI TV-@nywhere Master", - .tuner_type = 33, + .tuner_type = TUNER_MT2032, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -206,7 +230,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_WINFAST_DV2000] = { .name = "Leadtek Winfast DV2000", - .tuner_type = 38, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -239,34 +266,40 @@ struct cx88_board cx88_boards[] = { .gpio3 = 0x02000000, }, }, - [CX88_BOARD_LEADTEK_PVR2000] = { + [CX88_BOARD_LEADTEK_PVR2000] = { // gpio values for PAL version from regspy by DScaler - .name = "Leadtek PVR 2000", - .tuner_type = 38, + .name = "Leadtek PVR 2000", + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x0000bde6, - },{ - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x0000bde6, - },{ - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x0000bde6, - }}, - .radio = { - .type = CX88_RADIO, - .gpio0 = 0x0000bd62, - }, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0000bde2, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0000bde6, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x0000bde6, + }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x0000bd62, + }, .blackbird = 1, - }, + }, [CX88_BOARD_IODATA_GVVCP3PCI] = { .name = "IODATA GV-VCP3/PCI", .tuner_type = TUNER_ABSENT, - .input = {{ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 0, },{ @@ -279,7 +312,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_PROLINK_PLAYTVPVR] = { .name = "Prolink PlayTV PVR", - .tuner_type = 43, + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -301,8 +337,11 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_ASUS_PVR_416] = { .name = "ASUS PVR-416", - .tuner_type = 43, - .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -320,7 +359,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_MSI_TVANYWHERE] = { .name = "MSI TV-@nywhere", - .tuner_type = 33, + .tuner_type = TUNER_MT2032, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -342,6 +384,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_KWORLD_DVB_T] = { .name = "KWorld/VStream XPert DVB-T", .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -358,6 +403,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = { .name = "DVICO FusionHDTV DVB-T1", .tuner_type = TUNER_ABSENT, /* No analog tuner */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -371,7 +419,10 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_KWORLD_LTV883] = { .name = "KWorld LTV883RF", - .tuner_type = 48, + .tuner_type = TUNER_TNF_8831BGFF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -397,6 +448,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD] = { .name = "DViCO - FusionHDTV 3 Gold", .tuner_type = TUNER_MICROTUNE_4042FI5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, /* GPIO[0] resets DT3302 DTV receiver 0 - reset asserted @@ -428,17 +482,14 @@ struct cx88_board cx88_boards[] = { .vmux = 2, .gpio0 = 0x0f00, }}, -#if 0 - .ts = { - .type = CX88_TS, - .gpio0 = 0x00000f01, /* Hooked to tuner reset bit */ - } -#endif }, [CX88_BOARD_HAUPPAUGE_DVB_T1] = { - .name = "Hauppauge Nova-T DVB-T", + .name = "Hauppauge Nova-T DVB-T", .tuner_type = TUNER_ABSENT, - .input = {{ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ .type = CX88_VMUX_DVB, .vmux = 0, }}, @@ -447,6 +498,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_CONEXANT_DVB_T1] = { .name = "Conexant DVB-T reference design", .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_DVB, .vmux = 0, @@ -456,6 +510,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_PROVIDEO_PV259] = { .name = "Provideo PV259", .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -465,6 +522,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = { .name = "DVICO FusionHDTV DVB-T Plus", .tuner_type = TUNER_ABSENT, /* No analog tuner */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -479,6 +539,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_DNTV_LIVE_DVB_T] = { .name = "digitalnow DNTV Live! DVB-T", .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -495,6 +558,9 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_PCHDTV_HD3000] = { .name = "pcHDTV HD3000 HDTV", .tuner_type = TUNER_THOMSON_DTT7610, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -530,8 +596,11 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_HAUPPAUGE_ROSLYN] = { // entry added by Kaustubh D. Bhalerao // GPIO values obtained from regspy, courtesy Sean Covel - .name = "Hauppauge WinTV 28xxx (Roslyn) models", - .tuner_type = UNSET, + .name = "Hauppauge WinTV 28xxx (Roslyn) models", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -559,33 +628,37 @@ struct cx88_board cx88_boards[] = { .blackbird = 1, }, [CX88_BOARD_DIGITALLOGIC_MEC] = { - /* params copied over from Leadtek PVR 2000 */ .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)", - /* not sure yet about the tuner type */ - .tuner_type = 38, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, - .gpio0 = 0x0000bde6, + .gpio0 = 0x00009d80, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - .gpio0 = 0x0000bde6, + .gpio0 = 0x00009d76, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, - .gpio0 = 0x0000bde6, + .gpio0 = 0x00009d76, }}, .radio = { .type = CX88_RADIO, - .gpio0 = 0x0000bd62, + .gpio0 = 0x00009d00, }, .blackbird = 1, }, [CX88_BOARD_IODATA_GVBCTV7E] = { .name = "IODATA GV/BCTV7E", .tuner_type = TUNER_PHILIPS_FQ1286, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, @@ -601,6 +674,56 @@ struct cx88_board cx88_boards[] = { .gpio1 = 0x0000e07f, }} }, + [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = { + .name = "PixelView PlayTV Ultra Pro (Stereo)", + /* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */ + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0xc2>>1, + .radio_addr = 0xc0>>1, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0xbf61, /* internal decoder */ + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0xbf63, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0xbf63, + }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0xbf60, + }, + }, + [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = { + .name = "DViCO - FusionHDTV 3 Gold-T", + .tuner_type = TUNER_THOMSON_DTT7611, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + /* See DViCO FusionHDTV 3 Gold for GPIO documentation. */ + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0f0d, + },{ + .type = CX88_VMUX_CABLE, + .vmux = 0, + .gpio0 = 0x0f05, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0f00, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x0f00, + }}, + }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -672,6 +795,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x18ac, .subdevice = 0xd810, .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD, + },{ + .subvendor = 0x18ac, + .subdevice = 0xd820, + .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T, },{ .subvendor = 0x18AC, .subdevice = 0xDB00, @@ -935,4 +1062,5 @@ EXPORT_SYMBOL(cx88_card_setup); * Local variables: * c-basic-offset: 8 * End: + * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off */ diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 1ff79b5a8835..c046a23537d3 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-core.c,v 1.24 2005/01/19 12:01:55 kraxel Exp $ + * $Id: cx88-core.c,v 1.28 2005/06/12 04:19:19 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * driver core @@ -51,12 +51,15 @@ module_param(latency,int,0444); MODULE_PARM_DESC(latency,"pci latency timer"); static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; module_param_array(tuner, int, NULL, 0444); +module_param_array(radio, int, NULL, 0444); module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(tuner,"tuner type"); +MODULE_PARM_DESC(radio,"radio tuner type"); MODULE_PARM_DESC(card,"card type"); static unsigned int nicam = 0; @@ -429,7 +432,7 @@ int cx88_sram_channel_setup(struct cx88_core *core, /* ------------------------------------------------------------------ */ /* debug helper code */ -static int cx88_risc_decode(u32 risc) +int cx88_risc_decode(u32 risc) { static char *instr[16] = { [ RISC_SYNC >> 28 ] = "sync", @@ -736,6 +739,10 @@ static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm) { static const unsigned int ntsc = 28636360; static const unsigned int pal = 35468950; + static const unsigned int palm = 28604892; + + if (norm->id & V4L2_STD_PAL_M) + return palm; return (norm->id & V4L2_STD_625_50) ? pal : ntsc; } @@ -749,6 +756,11 @@ static unsigned int inline norm_notchfilter(struct cx88_tvnorm *norm) static unsigned int inline norm_htotal(struct cx88_tvnorm *norm) { + /* Should always be Line Draw Time / (4*FSC) */ + + if (norm->id & V4L2_STD_PAL_M) + return 909; + return (norm->id & V4L2_STD_625_50) ? 1135 : 910; } @@ -1164,8 +1176,20 @@ struct cx88_core* cx88_core_get(struct pci_dev *pci) "insmod option" : "autodetected"); core->tuner_type = tuner[core->nr]; + core->radio_type = radio[core->nr]; if (UNSET == core->tuner_type) core->tuner_type = cx88_boards[core->board].tuner_type; + if (UNSET == core->radio_type) + core->radio_type = cx88_boards[core->board].radio_type; + if (!core->tuner_addr) + core->tuner_addr = cx88_boards[core->board].tuner_addr; + if (!core->radio_addr) + core->radio_addr = cx88_boards[core->board].radio_addr; + + printk(KERN_INFO "TV tuner %d at 0x%02x, Radio tuner %d at 0x%02x\n", + core->tuner_type, core->tuner_addr<<1, + core->radio_type, core->radio_addr<<1); + core->tda9887_conf = cx88_boards[core->board].tda9887_conf; /* init hardware */ diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 9d15d3d5a2b7..1a259c3966cd 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-dvb.c,v 1.31 2005/03/07 15:58:05 kraxel Exp $ + * $Id: cx88-dvb.c,v 1.33 2005/06/12 04:19:19 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * MPEG Transport Stream (DVB) routines diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 0725b1288f4f..e20adefcfc6c 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -1,5 +1,5 @@ /* - $Id: cx88-i2c.c,v 1.20 2005/02/15 15:59:35 kraxel Exp $ + $Id: cx88-i2c.c,v 1.23 2005/06/12 04:19:19 mchehab Exp $ cx88-i2c.c -- all the i2c code is here @@ -91,6 +91,7 @@ static int cx8800_bit_getsda(void *data) static int attach_inform(struct i2c_client *client) { + struct tuner_addr tun_addr; struct cx88_core *core = i2c_get_adapdata(client->adapter); dprintk(1, "i2c attach [addr=0x%x,client=%s]\n", @@ -98,8 +99,19 @@ static int attach_inform(struct i2c_client *client) if (!client->driver->command) return 0; - if (core->tuner_type != UNSET) - client->driver->command(client, TUNER_SET_TYPE, &core->tuner_type); + if (core->radio_type != UNSET) { + tun_addr.v4l2_tuner = V4L2_TUNER_RADIO; + tun_addr.type = core->radio_type; + tun_addr.addr = core->radio_addr; + client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); + } + if (core->tuner_type != UNSET) { + tun_addr.v4l2_tuner = V4L2_TUNER_ANALOG_TV; + tun_addr.type = core->tuner_type; + tun_addr.addr = core->tuner_addr; + client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); + } + if (core->tda9887_conf) client->driver->command(client, TDA9887_SET_CONFIG, &core->tda9887_conf); return 0; diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index af6ad8cdbdb7..dc0dcf249aac 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-input.c,v 1.9 2005/03/04 09:12:23 kraxel Exp $ + * $Id: cx88-input.c,v 1.11 2005/05/22 20:57:56 nsh Exp $ * * Device driver for GPIO attached remote control interfaces * on Conexant 2388x based TV/DVB cards. @@ -235,6 +235,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) /* detect & configure */ switch (core->board) { case CX88_BOARD_DNTV_LIVE_DVB_T: + case CX88_BOARD_KWORLD_DVB_T: ir_codes = ir_codes_dntv_live_dvb_t; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; @@ -261,7 +262,15 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keydown = 0x02; ir->polling = 5; // ms break; + case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: + ir_codes = ir_codes_pixelview; + ir->gpio_addr = MO_GP1_IO; + ir->mask_keycode = 0x1f; + ir->mask_keyup = 0x80; + ir->polling = 1; // ms + break; } + if (NULL == ir_codes) { kfree(ir); return -ENODEV; diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 07aae1899e17..9ade2ae91e9b 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-mpeg.c,v 1.25 2005/03/07 14:18:00 kraxel Exp $ + * $Id: cx88-mpeg.c,v 1.26 2005/06/03 13:31:51 mchehab Exp $ * * Support for the mpeg transport stream transfers * PCI function #2 of the cx2388x. @@ -55,7 +55,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev, { struct cx88_core *core = dev->core; - dprintk(1, "cx8802_start_mpegport_dma %d\n", buf->vb.width); + dprintk(0, "cx8802_start_dma %d\n", buf->vb.width); /* setup fifo + format */ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], @@ -100,18 +100,21 @@ static int cx8802_start_dma(struct cx8802_dev *dev, q->count = 1; /* enable irqs */ + dprintk( 0, "setting the interrupt mask\n" ); cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x04); - cx_write(MO_TS_INTMSK, 0x1f0011); + cx_set(MO_TS_INTMSK, 0x1f0011); + //cx_write(MO_TS_INTMSK, 0x0f0011); /* start dma */ - cx_write(MO_DEV_CNTRL2, (1<<5)); /* FIXME: s/write/set/ ??? */ - cx_write(MO_TS_DMACNTRL, 0x11); + cx_set(MO_DEV_CNTRL2, (1<<5)); + cx_set(MO_TS_DMACNTRL, 0x11); return 0; } static int cx8802_stop_dma(struct cx8802_dev *dev) { struct cx88_core *core = dev->core; + dprintk( 0, "cx8802_stop_dma\n" ); /* stop dma */ cx_clear(MO_TS_DMACNTRL, 0x11); @@ -131,8 +134,12 @@ static int cx8802_restart_queue(struct cx8802_dev *dev, struct cx88_buffer *buf; struct list_head *item; + dprintk( 0, "cx8802_restart_queue\n" ); if (list_empty(&q->active)) + { + dprintk( 0, "cx8802_restart_queue: queue is empty\n" ); return 0; + } buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); dprintk(2,"restart_queue [%p/%d]: restart dma\n", @@ -182,27 +189,32 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) struct cx88_buffer *prev; struct cx88_dmaqueue *q = &dev->mpegq; + dprintk( 1, "cx8802_buf_queue\n" ); /* add jump to stopper */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); if (list_empty(&q->active)) { + dprintk( 0, "queue is empty - first active\n" ); list_add_tail(&buf->vb.queue,&q->active); cx8802_start_dma(dev, q, buf); buf->vb.state = STATE_ACTIVE; buf->count = q->count++; mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(2,"[%p/%d] %s - first active\n", + dprintk(0,"[%p/%d] %s - first active\n", buf, buf->vb.i, __FUNCTION__); + //udelay(100); } else { + dprintk( 1, "queue is not empty - append to active\n" ); prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); list_add_tail(&buf->vb.queue,&q->active); buf->vb.state = STATE_ACTIVE; buf->count = q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - dprintk(2,"[%p/%d] %s - append to active\n", + dprintk( 1, "[%p/%d] %s - append to active\n", buf, buf->vb.i, __FUNCTION__); + //udelay(100); } } @@ -224,7 +236,10 @@ static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart) buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); } if (restart) + { + dprintk(0, "restarting queue\n" ); cx8802_restart_queue(dev,q); + } spin_unlock_irqrestore(&dev->slock,flags); } @@ -232,6 +247,7 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev) { struct cx88_dmaqueue *q = &dev->mpegq; + dprintk( 1, "cx8802_cancel_buffers" ); del_timer_sync(&q->timeout); cx8802_stop_dma(dev); do_cancel_buffers(dev,"cancel",0); @@ -241,7 +257,7 @@ static void cx8802_timeout(unsigned long data) { struct cx8802_dev *dev = (struct cx8802_dev*)data; - dprintk(1, "%s\n",__FUNCTION__); + dprintk(0, "%s\n",__FUNCTION__); if (debug) cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); @@ -254,12 +270,17 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) struct cx88_core *core = dev->core; u32 status, mask, count; + dprintk( 1, "cx8802_mpeg_irq\n" ); status = cx_read(MO_TS_INTSTAT); mask = cx_read(MO_TS_INTMSK); if (0 == (status & mask)) return; cx_write(MO_TS_INTSTAT, status); +#if 0 + cx88_print_irqbits(core->name, "irq mpeg ", + cx88_mpeg_irqs, status, mask); +#endif if (debug || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq mpeg ", cx88_mpeg_irqs, status, mask); @@ -273,6 +294,7 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) /* risc1 y */ if (status & 0x01) { + dprintk( 1, "wake up\n" ); spin_lock(&dev->slock); count = cx_read(MO_TS_GPCNT); cx88_wakeup(dev->core, &dev->mpegq, count); @@ -288,6 +310,7 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) /* other general errors */ if (status & 0x1f0100) { + dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 ); spin_lock(&dev->slock); cx8802_stop_dma(dev); cx8802_restart_queue(dev,&dev->mpegq); @@ -295,6 +318,8 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) } } +#define MAX_IRQ_LOOP 10 + static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) { struct cx8802_dev *dev = dev_id; @@ -302,10 +327,13 @@ static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) u32 status; int loop, handled = 0; - for (loop = 0; loop < 10; loop++) { + for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { status = cx_read(MO_PCI_INTSTAT) & (core->pci_irqmask | 0x04); if (0 == status) goto out; + dprintk( 1, "cx8802_irq\n" ); + dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP ); + dprintk( 1, " status: %d\n", status ); handled = 1; cx_write(MO_PCI_INTSTAT, status); @@ -314,7 +342,8 @@ static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) if (status & 0x04) cx8802_mpeg_irq(dev); }; - if (10 == loop) { + if (MAX_IRQ_LOOP == loop) { + dprintk( 0, "clearing mask\n" ); printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", core->name); cx_write(MO_PCI_INTMSK,0); @@ -378,6 +407,7 @@ int cx8802_init_common(struct cx8802_dev *dev) void cx8802_fini_common(struct cx8802_dev *dev) { + dprintk( 2, "cx8802_fini_common\n" ); cx8802_stop_dma(dev); pci_disable_device(dev->pci); @@ -399,6 +429,7 @@ int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) /* stop mpeg dma */ spin_lock(&dev->slock); if (!list_empty(&dev->mpegq.active)) { + dprintk( 2, "suspend\n" ); printk("%s: suspend mpeg\n", core->name); cx8802_stop_dma(dev); del_timer(&dev->mpegq.timeout); @@ -463,4 +494,5 @@ EXPORT_SYMBOL(cx8802_resume_common); * Local variables: * c-basic-offset: 8 * End: + * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off */ diff --git a/drivers/media/video/cx88/cx88-reg.h b/drivers/media/video/cx88/cx88-reg.h index 8638ce57d84c..63ad33f5818b 100644 --- a/drivers/media/video/cx88/cx88-reg.h +++ b/drivers/media/video/cx88/cx88-reg.h @@ -1,5 +1,5 @@ /* - $Id: cx88-reg.h,v 1.6 2004/10/13 10:39:00 kraxel Exp $ + $Id: cx88-reg.h,v 1.7 2005/06/03 13:31:51 mchehab Exp $ cx88x-hw.h - CX2388x register offsets @@ -397,6 +397,7 @@ #define AUD_RATE_ADJ4 0x3205e4 #define AUD_RATE_ADJ5 0x3205e8 #define AUD_APB_IN_RATE_ADJ 0x3205ec +#define AUD_I2SCNTL 0x3205ec #define AUD_PHASE_FIX_CTL 0x3205f0 #define AUD_PLL_PRESCALE 0x320600 #define AUD_PLL_DDS 0x320604 diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index f2a9475a2fee..46d78b1dc9b2 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -1,5 +1,5 @@ /* - $Id: cx88-tvaudio.c,v 1.34 2005/03/07 16:10:51 kraxel Exp $ + $Id: cx88-tvaudio.c,v 1.36 2005/06/05 05:53:45 mchehab Exp $ cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver @@ -127,7 +127,8 @@ static void set_audio_start(struct cx88_core *core, cx_write(AUD_VOL_CTL, (1 << 6)); // increase level of input by 12dB - cx_write(AUD_AFE_12DB_EN, 0x0001); +// cx_write(AUD_AFE_12DB_EN, 0x0001); + cx_write(AUD_AFE_12DB_EN, 0x0000); // start programming cx_write(AUD_CTL, 0x0000); @@ -143,9 +144,15 @@ static void set_audio_finish(struct cx88_core *core) u32 volume; if (cx88_boards[core->board].blackbird) { + // sets sound input from external adc + cx_set(AUD_CTL, EN_I2SIN_ENABLE); + //cx_write(AUD_I2SINPUTCNTL, 0); + cx_write(AUD_I2SINPUTCNTL, 4); + cx_write(AUD_BAUDRATE, 1); // 'pass-thru mode': this enables the i2s output to the mpeg encoder - cx_set(AUD_CTL, 0x2000); + cx_set(AUD_CTL, EN_I2SOUT_ENABLE); cx_write(AUD_I2SOUTPUTCNTL, 1); + cx_write(AUD_I2SCNTL, 0); //cx_write(AUD_APB_IN_RATE_ADJ, 0); } @@ -707,50 +714,65 @@ static void set_audio_standard_EIAJ(struct cx88_core *core) set_audio_finish(core); } -static void set_audio_standard_FM(struct cx88_core *core) +static void set_audio_standard_FM(struct cx88_core *core, enum cx88_deemph_type deemph) { -#if 0 /* FIXME */ - switch (dev->audio_properties.FM_deemphasis) - { - case WW_FM_DEEMPH_50: - //Set De-emphasis filter coefficients for 50 usec - cx_write(AUD_DEEMPH0_G0, 0x0C45); - cx_write(AUD_DEEMPH0_A0, 0x6262); - cx_write(AUD_DEEMPH0_B0, 0x1C29); - cx_write(AUD_DEEMPH0_A1, 0x3FC66); - cx_write(AUD_DEEMPH0_B1, 0x399A); + static const struct rlist fm_deemph_50[] = { + { AUD_DEEMPH0_G0, 0x0C45 }, + { AUD_DEEMPH0_A0, 0x6262 }, + { AUD_DEEMPH0_B0, 0x1C29 }, + { AUD_DEEMPH0_A1, 0x3FC66}, + { AUD_DEEMPH0_B1, 0x399A }, - cx_write(AUD_DEEMPH1_G0, 0x0D80); - cx_write(AUD_DEEMPH1_A0, 0x6262); - cx_write(AUD_DEEMPH1_B0, 0x1C29); - cx_write(AUD_DEEMPH1_A1, 0x3FC66); - cx_write(AUD_DEEMPH1_B1, 0x399A); + { AUD_DEEMPH1_G0, 0x0D80 }, + { AUD_DEEMPH1_A0, 0x6262 }, + { AUD_DEEMPH1_B0, 0x1C29 }, + { AUD_DEEMPH1_A1, 0x3FC66}, + { AUD_DEEMPH1_B1, 0x399A}, - break; + { AUD_POLYPH80SCALEFAC, 0x0003}, + { /* end of list */ }, + }; + static const struct rlist fm_deemph_75[] = { + { AUD_DEEMPH0_G0, 0x091B }, + { AUD_DEEMPH0_A0, 0x6B68 }, + { AUD_DEEMPH0_B0, 0x11EC }, + { AUD_DEEMPH0_A1, 0x3FC66}, + { AUD_DEEMPH0_B1, 0x399A }, - case WW_FM_DEEMPH_75: - //Set De-emphasis filter coefficients for 75 usec - cx_write(AUD_DEEMPH0_G0, 0x91B ); - cx_write(AUD_DEEMPH0_A0, 0x6B68); - cx_write(AUD_DEEMPH0_B0, 0x11EC); - cx_write(AUD_DEEMPH0_A1, 0x3FC66); - cx_write(AUD_DEEMPH0_B1, 0x399A); + { AUD_DEEMPH1_G0, 0x0AA0 }, + { AUD_DEEMPH1_A0, 0x6B68 }, + { AUD_DEEMPH1_B0, 0x11EC }, + { AUD_DEEMPH1_A1, 0x3FC66}, + { AUD_DEEMPH1_B1, 0x399A}, - cx_write(AUD_DEEMPH1_G0, 0xAA0 ); - cx_write(AUD_DEEMPH1_A0, 0x6B68); - cx_write(AUD_DEEMPH1_B0, 0x11EC); - cx_write(AUD_DEEMPH1_A1, 0x3FC66); - cx_write(AUD_DEEMPH1_B1, 0x399A); + { AUD_POLYPH80SCALEFAC, 0x0003}, + { /* end of list */ }, + }; - break; - } -#endif + /* It is enough to leave default values? */ + static const struct rlist fm_no_deemph[] = { + + { AUD_POLYPH80SCALEFAC, 0x0003}, + { /* end of list */ }, + }; dprintk("%s (status: unknown)\n",__FUNCTION__); set_audio_start(core, 0x0020, EN_FMRADIO_AUTO_STEREO); - // AB: 10/2/01: this register is not being reset appropriately on occasion. - cx_write(AUD_POLYPH80SCALEFAC,3); + switch (deemph) + { + case FM_NO_DEEMPH: + set_audio_registers(core, fm_no_deemph); + break; + + case FM_DEEMPH_50: + set_audio_registers(core, fm_deemph_50); + break; + + case FM_DEEMPH_75: + set_audio_registers(core, fm_deemph_75); + break; + } set_audio_finish(core); } @@ -778,7 +800,7 @@ void cx88_set_tvaudio(struct cx88_core *core) set_audio_standard_EIAJ(core); break; case WW_FM: - set_audio_standard_FM(core); + set_audio_standard_FM(core,FM_NO_DEEMPH); break; case WW_SYSTEM_L_AM: set_audio_standard_NICAM_L(core, 1); @@ -1029,4 +1051,5 @@ EXPORT_SYMBOL(cx88_audio_thread); * Local variables: * c-basic-offset: 8 * End: + * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off */ diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c index 0584ff476387..320d57888bbd 100644 --- a/drivers/media/video/cx88/cx88-vbi.c +++ b/drivers/media/video/cx88/cx88-vbi.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-vbi.c,v 1.16 2004/12/10 12:33:39 kraxel Exp $ + * $Id: cx88-vbi.c,v 1.17 2005/06/12 04:19:19 mchehab Exp $ */ #include #include @@ -47,8 +47,8 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) } static int cx8800_start_vbi_dma(struct cx8800_dev *dev, - struct cx88_dmaqueue *q, - struct cx88_buffer *buf) + struct cx88_dmaqueue *q, + struct cx88_buffer *buf) { struct cx88_core *core = dev->core; diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index d1f5c92f0ce5..e4ca7350df15 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1,5 +1,5 @@ /* - * $Id: cx88-video.c,v 1.58 2005/03/07 15:58:05 kraxel Exp $ + * $Id: cx88-video.c,v 1.63 2005/06/12 04:19:19 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * video4linux video interface @@ -1187,9 +1187,24 @@ static void init_controls(struct cx8800_dev *dev) .id = V4L2_CID_AUDIO_VOLUME, .value = 0x3f, }; + static struct v4l2_control hue = { + .id = V4L2_CID_HUE, + .value = 0x80, + }; + static struct v4l2_control contrast = { + .id = V4L2_CID_CONTRAST, + .value = 0x80, + }; + static struct v4l2_control brightness = { + .id = V4L2_CID_BRIGHTNESS, + .value = 0x80, + }; set_control(dev,&mute); set_control(dev,&volume); + set_control(dev,&hue); + set_control(dev,&contrast); + set_control(dev,&brightness); } /* ------------------------------------------------------------------ */ @@ -1335,6 +1350,9 @@ static int video_do_ioctl(struct inode *inode, struct file *file, V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VBI_CAPTURE | +#if 0 + V4L2_TUNER_CAP_LOW | +#endif #if 0 V4L2_CAP_VIDEO_OVERLAY | #endif @@ -1696,7 +1714,11 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); cap->version = CX88_VERSION_CODE; - cap->capabilities = V4L2_CAP_TUNER; + cap->capabilities = V4L2_CAP_TUNER +#if 0 + | V4L2_TUNER_CAP_LOW +#endif + ; return 0; } case VIDIOC_G_TUNER: @@ -1992,6 +2014,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, { struct cx8800_dev *dev; struct cx88_core *core; + struct tuner_addr tun_addr; int err; dev = kmalloc(sizeof(*dev),GFP_KERNEL); @@ -2065,8 +2088,19 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, request_module("tuner"); if (core->tda9887_conf) request_module("tda9887"); - if (core->tuner_type != UNSET) - cx88_call_i2c_clients(dev->core,TUNER_SET_TYPE,&core->tuner_type); + if (core->radio_type != UNSET) { + tun_addr.v4l2_tuner = V4L2_TUNER_RADIO; + tun_addr.type = core->radio_type; + tun_addr.addr = core->radio_addr; + cx88_call_i2c_clients(dev->core,TUNER_SET_TYPE_ADDR, &tun_addr); + } + if (core->tuner_type != UNSET) { + tun_addr.v4l2_tuner = V4L2_TUNER_ANALOG_TV; + tun_addr.type = core->tuner_type; + tun_addr.addr = core->tuner_addr; + cx88_call_i2c_clients(dev->core,TUNER_SET_TYPE_ADDR, &tun_addr); + } + if (core->tda9887_conf) cx88_call_i2c_clients(dev->core,TDA9887_SET_CONFIG,&core->tda9887_conf); @@ -2162,7 +2196,7 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; /* stop video+vbi capture */ @@ -2194,7 +2228,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) static int cx8800_resume(struct pci_dev *pci_dev) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; if (dev->state.disabled) { @@ -2230,8 +2264,8 @@ static struct pci_device_id cx8800_pci_tbl[] = { { .vendor = 0x14f1, .device = 0x8800, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, },{ /* --- end of list --- */ } @@ -2239,10 +2273,10 @@ static struct pci_device_id cx8800_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); static struct pci_driver cx8800_pci_driver = { - .name = "cx8800", - .id_table = cx8800_pci_tbl, - .probe = cx8800_initdev, - .remove = __devexit_p(cx8800_finidev), + .name = "cx8800", + .id_table = cx8800_pci_tbl, + .probe = cx8800_initdev, + .remove = __devexit_p(cx8800_finidev), .suspend = cx8800_suspend, .resume = cx8800_resume, @@ -2274,4 +2308,5 @@ module_exit(cx8800_fini); * Local variables: * c-basic-offset: 8 * End: + * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off */ diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 88eaaaba5ad8..ac0dc27bb38f 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -1,5 +1,5 @@ /* - * $Id: cx88.h,v 1.56 2005/03/04 09:12:23 kraxel Exp $ + * $Id: cx88.h,v 1.62 2005/06/12 04:19:19 mchehab Exp $ * * v4l2 device driver for cx2388x based TV cards * @@ -64,6 +64,13 @@ #define SHADOW_AUD_BAL_CTL 2 #define SHADOW_MAX 2 +/* FM Radio deemphasis type */ +enum cx88_deemph_type { + FM_NO_DEEMPH = 0, + FM_DEEMPH_50, + FM_DEEMPH_75 +}; + /* ----------------------------------------------------------- */ /* tv norms */ @@ -162,6 +169,8 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_HAUPPAUGE_ROSLYN 24 #define CX88_BOARD_DIGITALLOGIC_MEC 25 #define CX88_BOARD_IODATA_GVBCTV7E 26 +#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27 +#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -185,6 +194,9 @@ struct cx88_input { struct cx88_board { char *name; unsigned int tuner_type; + unsigned int radio_type; + unsigned char tuner_addr; + unsigned char radio_addr; int tda9887_conf; struct cx88_input input[8]; struct cx88_input radio; @@ -255,6 +267,9 @@ struct cx88_core { /* config info -- analog */ unsigned int board; unsigned int tuner_type; + unsigned int radio_type; + unsigned char tuner_addr; + unsigned char radio_addr; unsigned int tda9887_conf; unsigned int has_radio; @@ -420,6 +435,7 @@ struct cx8802_dev { /* ----------------------------------------------------------- */ /* cx88-core.c */ +extern char *cx88_pci_irqs[32]; extern char *cx88_vid_irqs[32]; extern char *cx88_mpeg_irqs[32]; extern void cx88_print_irqbits(char *name, char *tag, char **strings, @@ -471,6 +487,11 @@ extern void cx88_core_put(struct cx88_core *core, /* cx88-vbi.c */ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f); +/* +int cx8800_start_vbi_dma(struct cx8800_dev *dev, + struct cx88_dmaqueue *q, + struct cx88_buffer *buf); +*/ int cx8800_stop_vbi_dma(struct cx8800_dev *dev); int cx8800_restart_vbi_queue(struct cx8800_dev *dev, struct cx88_dmaqueue *q); diff --git a/drivers/media/video/ir-kbd-gpio.c b/drivers/media/video/ir-kbd-gpio.c index ab6620de4b3b..a565823330aa 100644 --- a/drivers/media/video/ir-kbd-gpio.c +++ b/drivers/media/video/ir-kbd-gpio.c @@ -1,5 +1,5 @@ /* - * $Id: ir-kbd-gpio.c,v 1.12 2005/02/22 12:28:40 kraxel Exp $ + * $Id: ir-kbd-gpio.c,v 1.13 2005/05/15 19:01:26 mchehab Exp $ * * Copyright (c) 2003 Gerd Knorr * Copyright (c) 2003 Pavel Machek @@ -114,38 +114,6 @@ static IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = { [ 0x3e ] = KEY_VOLUMEUP, // 'volume +' }; -static IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE] = { - [ 2 ] = KEY_KP0, - [ 1 ] = KEY_KP1, - [ 11 ] = KEY_KP2, - [ 27 ] = KEY_KP3, - [ 5 ] = KEY_KP4, - [ 9 ] = KEY_KP5, - [ 21 ] = KEY_KP6, - [ 6 ] = KEY_KP7, - [ 10 ] = KEY_KP8, - [ 18 ] = KEY_KP9, - - [ 3 ] = KEY_TUNER, // TV/FM - [ 7 ] = KEY_SEARCH, // scan - [ 28 ] = KEY_ZOOM, // full screen - [ 30 ] = KEY_POWER, - [ 23 ] = KEY_VOLUMEDOWN, - [ 31 ] = KEY_VOLUMEUP, - [ 20 ] = KEY_CHANNELDOWN, - [ 22 ] = KEY_CHANNELUP, - [ 24 ] = KEY_MUTE, - - [ 0 ] = KEY_LIST, // source - [ 19 ] = KEY_INFO, // loop - [ 16 ] = KEY_LAST, // +100 - [ 13 ] = KEY_CLEAR, // reset - [ 12 ] = BTN_RIGHT, // fun++ - [ 4 ] = BTN_LEFT, // fun-- - [ 14 ] = KEY_GOTO, // function - [ 15 ] = KEY_STOP, // freeze -}; - /* Attila Kondoros */ static IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = { diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index 09464d624a6b..1b7d38e96f14 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -735,7 +735,6 @@ static int msp34xx_sleep(struct msp3400c *msp, int timeout) { DECLARE_WAITQUEUE(wait, current); -again: add_wait_queue(&msp->wq, &wait); if (!kthread_should_stop()) { if (timeout < 0) { @@ -751,12 +750,9 @@ again: #endif } } - + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); remove_wait_queue(&msp->wq, &wait); - - if (try_to_freeze(PF_FREEZE)) - goto again; - return msp->restart; } @@ -1436,7 +1432,7 @@ static int msp_detach(struct i2c_client *client); static int msp_probe(struct i2c_adapter *adap); static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); -static int msp_suspend(struct device * dev, pm_message_t state, u32 level); +static int msp_suspend(struct device * dev, u32 state, u32 level); static int msp_resume(struct device * dev, u32 level); static void msp_wake_thread(struct i2c_client *client); @@ -1841,7 +1837,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int msp_suspend(struct device * dev, pm_message_t state, u32 level) +static int msp_suspend(struct device * dev, u32 state, u32 level) { struct i2c_client *c = container_of(dev, struct i2c_client, dev); diff --git a/drivers/media/video/msp3400.h b/drivers/media/video/msp3400.h index d70a954e13aa..023f33056a4f 100644 --- a/drivers/media/video/msp3400.h +++ b/drivers/media/video/msp3400.h @@ -1,3 +1,7 @@ +/* + * $Id: msp3400.h,v 1.3 2005/06/12 04:19:19 mchehab Exp $ + */ + #ifndef MSP3400_H #define MSP3400_H diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index 42c2b565c9fe..e6d0a18833d6 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -41,16 +41,16 @@ enum saa6752hs_videoformat { static const struct v4l2_format v4l2_format_table[] = { - [SAA6752HS_VF_D1] = { - .fmt = { .pix = { .width = 720, .height = 576 }, }, }, - [SAA6752HS_VF_2_3_D1] = { - .fmt = { .pix = { .width = 480, .height = 576 }, }, }, - [SAA6752HS_VF_1_2_D1] = { - .fmt = { .pix = { .width = 352, .height = 576 }, }, }, - [SAA6752HS_VF_SIF] = { - .fmt = { .pix = { .width = 352, .height = 288 }, }, }, - [SAA6752HS_VF_UNKNOWN] = { - .fmt = { .pix = { .width = 0, .height = 0 }, }, }, + [SAA6752HS_VF_D1] = + { .fmt = { .pix = { .width = 720, .height = 576 }}}, + [SAA6752HS_VF_2_3_D1] = + { .fmt = { .pix = { .width = 480, .height = 576 }}}, + [SAA6752HS_VF_1_2_D1] = + { .fmt = { .pix = { .width = 352, .height = 576 }}}, + [SAA6752HS_VF_SIF] = + { .fmt = { .pix = { .width = 352, .height = 288 }}}, + [SAA6752HS_VF_UNKNOWN] = + { .fmt = { .pix = { .width = 0, .height = 0}}}, }; struct saa6752hs_state { diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index c51eb7f078d3..0c781e24c446 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1,6 +1,6 @@ /* - * $Id: saa7134-cards.c,v 1.54 2005/03/07 12:01:51 kraxel Exp $ + * $Id: saa7134-cards.c,v 1.58 2005/06/07 18:05:00 nsh Exp $ * * device driver for philips saa7134 based TV cards * card-specific stuff. @@ -165,7 +165,7 @@ struct saa7134_board saa7134_boards[] = { .inputs = {{ .name = name_tv, .vmux = 1, - .amux = LINE2, + .amux = TV, .tv = 1, },{ .name = name_comp1, @@ -878,7 +878,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV002] = { /* Ognjen Nastic */ - .name = "Manli MuchTV M-TV002", + .name = "Manli MuchTV M-TV002/Behold TV 403 FM", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .inputs = {{ @@ -899,14 +899,10 @@ struct saa7134_board saa7134_boards[] = { .name = name_radio, .amux = LINE2, }, - .mute = { - .name = name_mute, - .amux = LINE1, - }, }, [SAA7134_BOARD_MANLI_MTV001] = { /* Ognjen Nastic UNTESTED */ - .name = "Manli MuchTV M-TV001", + .name = "Manli MuchTV M-TV001/Behold TV 401", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .inputs = {{ @@ -923,6 +919,10 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, .tv = 1, }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, }, [SAA7134_BOARD_TG3000TV] = { /* TransGear 3000TV */ @@ -1078,7 +1078,6 @@ struct saa7134_board saa7134_boards[] = { .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1256_IH3, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0x3, .inputs = {{ .name = name_tv, .vmux = 1, @@ -1285,7 +1284,7 @@ struct saa7134_board saa7134_boards[] = { .gpio =0x8000, } }, - [SAA7134_BOARD_AVERMEDIA_307] = { + [SAA7134_BOARD_AVERMEDIA_STUDIO_307] = { /* Nickolay V. Shmyrev Lots of thanks to Andrey Zolotarev @@ -1323,6 +1322,35 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x01, }, }, + [SAA7134_BOARD_AVERMEDIA_GO_007_FM] = { + .name = "Avermedia AVerTV GO 007 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .gpiomask = 0x00300003, +// .gpiomask = 0x8c240003, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x01, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x02, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + .gpio = 0x02, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x00300001, + }, + }, [SAA7134_BOARD_AVERMEDIA_CARDBUS] = { /* Jon Westgate */ .name = "AVerMedia Cardbus TV/Radio", @@ -1492,7 +1520,6 @@ struct saa7134_board saa7134_boards[] = { .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0x3, .inputs = {{ .name = name_tv, .vmux = 1, @@ -1546,7 +1573,82 @@ struct saa7134_board saa7134_boards[] = { // .gpio = 0x4000, }}, }, -}; + [SAA7134_BOARD_AVERMEDIA_307] = { + /* + Davydov Vladimir + */ + .name = "Avermedia AVerTV 307", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_ADS_INSTANT_TV] = { + .name = "ADS Tech Instant TV (saa7135)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_KWORLD_VSTREAM_XPERT] = { + .name = "Kworld/Tevion V-Stream Xpert TV PVR7134", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL_I, + .gpiomask = 0x0700, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, //gpio by DScaler + },{ + .name = name_svideo, + .vmux = 0, + .amux = LINE1, + .gpio = 0x200, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x100, + }, + }, + }; + const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); /* ------------------------------------------------------------------ */ @@ -1663,7 +1765,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, },{ .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, .subvendor = PCI_VENDOR_ID_ASUSTEK, .subdevice = 0x4845, .driver_data = SAA7135_BOARD_ASUSTeK_TVFM7135, @@ -1824,6 +1926,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0x9715, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_307, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa70a, .driver_data = SAA7134_BOARD_AVERMEDIA_307, },{ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -1844,6 +1952,26 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x5168, .subdevice = 0x0306, .driver_data = SAA7134_BOARD_FLYDVBTDUO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31f, + .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, + .subvendor = 0x1421, + .subdevice = 0x0350, /* PCI version */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, + .subvendor = 0x1421, + .subdevice = 0x0370, /* cardbus version */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, },{ /* --- boards without eeprom + subsystem ID --- */ @@ -1954,20 +2082,23 @@ int saa7134_board_init1(struct saa7134_dev *dev) dev->has_remote = 1; board_flyvideo(dev); break; - case SAA7134_BOARD_FLYTVPLATINUM_FM: + case SAA7134_BOARD_FLYTVPLATINUM_FM: case SAA7134_BOARD_CINERGY400: case SAA7134_BOARD_CINERGY600: case SAA7134_BOARD_CINERGY600_MK3: case SAA7134_BOARD_ECS_TVP3XP: case SAA7134_BOARD_ECS_TVP3XP_4CB5: case SAA7134_BOARD_MD2819: + case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: case SAA7134_BOARD_AVERMEDIA_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM: // case SAA7134_BOARD_SABRENT_SBTTVFM: /* not finished yet */ case SAA7134_BOARD_VIDEOMATE_TV_PVR: - dev->has_remote = 1; - break; + case SAA7134_BOARD_MANLI_MTV001: + case SAA7134_BOARD_MANLI_MTV002: case SAA7134_BOARD_AVACSSMARTTV: dev->has_remote = 1; break; diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index d506cafba8ff..f61ed1849a2a 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-core.c,v 1.28 2005/02/22 09:56:29 kraxel Exp $ + * $Id: saa7134-core.c,v 1.30 2005/05/22 19:23:39 nsh Exp $ * * device driver for philips saa7134 based TV cards * driver core @@ -316,7 +316,7 @@ unsigned long saa7134_buffer_base(struct saa7134_buf *buf) int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) { - u32 *cpu; + __le32 *cpu; dma_addr_t dma_addr; cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); @@ -332,7 +332,7 @@ int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, struct scatterlist *list, unsigned int length, unsigned int startpage) { - u32 *ptr; + __le32 *ptr; unsigned int i,p; BUG_ON(NULL == pt || NULL == pt->cpu); @@ -340,7 +340,7 @@ int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, ptr = pt->cpu + startpage; for (i = 0; i < length; i++, list++) for (p = 0; p * 4096 < list->length; p++, ptr++) - *ptr = sg_dma_address(list) - list->offset; + *ptr = cpu_to_le32(sg_dma_address(list) - list->offset); return 0; } diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index c2873ae029f9..aa8e2cf62d55 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-dvb.c,v 1.12 2005/02/18 12:28:29 kraxel Exp $ + * $Id: saa7134-dvb.c,v 1.13 2005/06/12 04:19:19 mchehab Exp $ * * (c) 2004 Gerd Knorr [SuSE Labs] * diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index fa1357336907..c85348d0239f 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-empress.c,v 1.10 2005/02/03 10:24:33 kraxel Exp $ + * $Id: saa7134-empress.c,v 1.11 2005/05/22 19:23:39 nsh Exp $ * * (c) 2004 Gerd Knorr [SuSE Labs] * diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 702bb63d9813..b6f002e8421d 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-i2c.c,v 1.10 2005/01/24 17:37:23 kraxel Exp $ + * $Id: saa7134-i2c.c,v 1.11 2005/06/12 01:36:14 mchehab Exp $ * * device driver for philips saa7134 based TV cards * i2c interface support diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index ca50cf531f20..aba2b9de60de 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-input.c,v 1.16 2004/12/10 12:33:39 kraxel Exp $ + * $Id: saa7134-input.c,v 1.19 2005/06/07 18:02:26 nsh Exp $ * * handle saa7134 IR remotes via linux kernel input layer. * @@ -308,6 +308,102 @@ static IR_KEYTAB_TYPE videomate_tv_pvr_codes[IR_KEYTAB_SIZE] = { [ 32 ] = KEY_LANGUAGE, [ 33 ] = KEY_SLEEP, }; + +/* Michael Tokarev + http://www.corpit.ru/mjt/beholdTV/remote_control.jpg + keytable is used by MANLI MTV00[12] and BeholdTV 40[13] at + least, and probably other cards too. + The "ascii-art picture" below (in comments, first row + is the keycode in hex, and subsequent row(s) shows + the button labels (several variants when appropriate) + helps to descide which keycodes to assign to the buttons. + */ +static IR_KEYTAB_TYPE manli_codes[IR_KEYTAB_SIZE] = { + + /* 0x1c 0x12 * + * FUNCTION POWER * + * FM (|) * + * */ + [ 0x1c ] = KEY_RADIO, /*XXX*/ + [ 0x12 ] = KEY_POWER, + + /* 0x01 0x02 0x03 * + * 1 2 3 * + * * + * 0x04 0x05 0x06 * + * 4 5 6 * + * * + * 0x07 0x08 0x09 * + * 7 8 9 * + * */ + [ 0x01 ] = KEY_KP1, + [ 0x02 ] = KEY_KP2, + [ 0x03 ] = KEY_KP3, + [ 0x04 ] = KEY_KP4, + [ 0x05 ] = KEY_KP5, + [ 0x06 ] = KEY_KP6, + [ 0x07 ] = KEY_KP7, + [ 0x08 ] = KEY_KP8, + [ 0x09 ] = KEY_KP9, + + /* 0x0a 0x00 0x17 * + * RECALL 0 +100 * + * PLUS * + * */ + [ 0x0a ] = KEY_AGAIN, /*XXX KEY_REWIND? */ + [ 0x00 ] = KEY_KP0, + [ 0x17 ] = KEY_DIGITS, /*XXX*/ + + /* 0x14 0x10 * + * MENU INFO * + * OSD */ + [ 0x14 ] = KEY_MENU, + [ 0x10 ] = KEY_INFO, + + /* 0x0b * + * Up * + * * + * 0x18 0x16 0x0c * + * Left Ok Right * + * * + * 0x015 * + * Down * + * */ + [ 0x0b ] = KEY_UP, /*XXX KEY_SCROLLUP? */ + [ 0x18 ] = KEY_LEFT, /*XXX KEY_BACK? */ + [ 0x16 ] = KEY_OK, /*XXX KEY_SELECT? KEY_ENTER? */ + [ 0x0c ] = KEY_RIGHT, /*XXX KEY_FORWARD? */ + [ 0x15 ] = KEY_DOWN, /*XXX KEY_SCROLLDOWN? */ + + /* 0x11 0x0d * + * TV/AV MODE * + * SOURCE STEREO * + * */ + [ 0x11 ] = KEY_TV, /*XXX*/ + [ 0x0d ] = KEY_MODE, /*XXX there's no KEY_STEREO */ + + /* 0x0f 0x1b 0x1a * + * AUDIO Vol+ Chan+ * + * TIMESHIFT??? * + * * + * 0x0e 0x1f 0x1e * + * SLEEP Vol- Chan- * + * */ + [ 0x0f ] = KEY_AUDIO, + [ 0x1b ] = KEY_VOLUMEUP, + [ 0x1a ] = KEY_CHANNELUP, + [ 0x0e ] = KEY_SLEEP, /*XXX maybe KEY_PAUSE */ + [ 0x1f ] = KEY_VOLUMEDOWN, + [ 0x1e ] = KEY_CHANNELDOWN, + + /* 0x13 0x19 * + * MUTE SNAPSHOT* + * */ + [ 0x13 ] = KEY_MUTE, + [ 0x19 ] = KEY_RECORD, /*XXX*/ + + // 0x1d unused ? +}; /* ---------------------------------------------------------------------- */ static int build_key(struct saa7134_dev *dev) @@ -379,7 +475,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_FLYVIDEO2000: case SAA7134_BOARD_FLYVIDEO3000: - case SAA7134_BOARD_FLYTVPLATINUM_FM: + case SAA7134_BOARD_FLYTVPLATINUM_FM: ir_codes = flyvideo_codes; mask_keycode = 0xEC00000; mask_keydown = 0x0040000; @@ -405,8 +501,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) polling = 50; // ms break; case SAA7134_BOARD_MD2819: + case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM: ir_codes = md2819_codes; mask_keycode = 0x0007C8; mask_keydown = 0x000010; @@ -415,6 +515,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPMODE0, 0x4); saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); break; + case SAA7134_BOARD_MANLI_MTV001: + case SAA7134_BOARD_MANLI_MTV002: + ir_codes = manli_codes; + mask_keycode = 0x001f00; + mask_keyup = 0x004000; + mask_keydown = 0x002000; + polling = 50; // ms + break; case SAA7134_BOARD_VIDEOMATE_TV_PVR: ir_codes = videomate_tv_pvr_codes; mask_keycode = 0x00003F; diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c index 6b6a643bf1cd..81732904623f 100644 --- a/drivers/media/video/saa7134/saa7134-oss.c +++ b/drivers/media/video/saa7134/saa7134-oss.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-oss.c,v 1.13 2004/12/10 12:33:39 kraxel Exp $ + * $Id: saa7134-oss.c,v 1.14 2005/05/18 22:45:16 hhackmann Exp $ * * device driver for philips saa7134 based TV cards * oss dsp interface @@ -49,7 +49,6 @@ MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)"); static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) { - blksize &= ~0xff; if (blksize < 0x100) blksize = 0x100; if (blksize > 0x10000) @@ -57,8 +56,6 @@ static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) if (blocks < 2) blocks = 2; - while ((blksize * blocks) & ~PAGE_MASK) - blocks++; if ((blksize * blocks) > 1024*1024) blocks = 1024*1024 / blksize; @@ -79,7 +76,7 @@ static int dsp_buffer_init(struct saa7134_dev *dev) BUG(); videobuf_dma_init(&dev->oss.dma); err = videobuf_dma_init_kernel(&dev->oss.dma, PCI_DMA_FROMDEVICE, - dev->oss.bufsize >> PAGE_SHIFT); + (dev->oss.bufsize + PAGE_SIZE) >> PAGE_SHIFT); if (0 != err) return err; return 0; @@ -163,10 +160,11 @@ static int dsp_rec_start(struct saa7134_dev *dev) fmt |= 0x04; fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80; - saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff)); - saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >> 8); - saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16); + saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->oss.blksize - 1) & 0x0000ff)); + saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->oss.blksize - 1) & 0x00ff00) >> 8); + saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->oss.blksize - 1) & 0xff0000) >> 16); saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); + break; case PCI_DEVICE_ID_PHILIPS_SAA7133: case PCI_DEVICE_ID_PHILIPS_SAA7135: @@ -817,7 +815,7 @@ void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status) reg = SAA7134_RS_BA1(6); } else { /* even */ - if (0 == (dev->oss.dma_blk & 0x00)) + if (1 == (dev->oss.dma_blk & 0x01)) reg = SAA7134_RS_BA2(6); } if (0 == reg) { diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index ecac13c006d5..3617e7f7a410 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-tvaudio.c,v 1.22 2005/01/07 13:11:19 kraxel Exp $ + * $Id: saa7134-tvaudio.c,v 1.25 2005/06/07 19:00:38 nsh Exp $ * * device driver for philips saa7134 based TV cards * tv audio decoder (fm stereo, nicam, ...) @@ -181,7 +181,8 @@ static void tvaudio_init(struct saa7134_dev *dev) saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff); saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff); saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff); - saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01); + // frame locked audio was reported not to be reliable + saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x02); saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14); saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50); @@ -250,6 +251,11 @@ static void mute_input_7134(struct saa7134_dev *dev) saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel); saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics); saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs); + // for oss, we need to change the clock configuration + if (in->amux == TV) + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); + else + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x01); /* switch gpio-connected external audio mux */ if (0 == card(dev).gpiomask) @@ -439,16 +445,15 @@ static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *au nicam = saa_readb(SAA7134_NICAM_STATUS); dprintk("getstereo: nicam=0x%x\n",nicam); switch (nicam & 0x0b) { + case 0x08: + retval = V4L2_TUNER_SUB_MONO; + break; case 0x09: retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; break; case 0x0a: retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; break; - case 0x08: - default: - retval = V4L2_TUNER_SUB_MONO; - break; } break; } @@ -572,14 +577,14 @@ static int tvaudio_thread(void *data) } else if (0 != dev->last_carrier) { /* no carrier -- try last detected one as fallback */ carrier = dev->last_carrier; - printk(KERN_WARNING "%s/audio: audio carrier scan failed, " + dprintk(KERN_WARNING "%s/audio: audio carrier scan failed, " "using %d.%03d MHz [last detected]\n", dev->name, carrier/1000, carrier%1000); } else { /* no carrier + no fallback -- use default */ carrier = default_carrier; - printk(KERN_WARNING "%s/audio: audio carrier scan failed, " + dprintk(KERN_WARNING "%s/audio: audio carrier scan failed, " "using %d.%03d MHz [default]\n", dev->name, carrier/1000, carrier%1000); } diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/video/saa7134/saa7134-vbi.c index 86954cc7c377..3c33c591cc85 100644 --- a/drivers/media/video/saa7134/saa7134-vbi.c +++ b/drivers/media/video/saa7134/saa7134-vbi.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-vbi.c,v 1.6 2004/12/10 12:33:39 kraxel Exp $ + * $Id: saa7134-vbi.c,v 1.7 2005/05/24 23:13:06 nsh Exp $ * * device driver for philips saa7134 based TV cards * video4linux video interface @@ -60,10 +60,10 @@ static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf, saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8); saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff); saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8); - saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start & 0xff); - saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start >> 8); - saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop & 0xff); - saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop >> 8); + saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start_0 & 0xff); + saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start_0 >> 8); + saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop_0 & 0xff); + saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop_0 >> 8); saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff); saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8); @@ -127,7 +127,7 @@ static int buffer_prepare(struct videobuf_queue *q, unsigned int lines, llength, size; int err; - lines = norm->vbi_v_stop - norm->vbi_v_start +1; + lines = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; if (lines > VBI_LINE_COUNT) lines = VBI_LINE_COUNT; #if 1 @@ -177,7 +177,7 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) struct saa7134_dev *dev = fh->dev; int llength,lines; - lines = dev->tvnorm->vbi_v_stop - dev->tvnorm->vbi_v_start +1; + lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1; #if 1 llength = VBI_LINE_LENGTH; #else diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 5d66060026ff..c0a2ee520531 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1,5 +1,5 @@ /* - * $Id: saa7134-video.c,v 1.28 2005/02/15 15:59:35 kraxel Exp $ + * $Id: saa7134-video.c,v 1.30 2005/06/07 19:00:38 nsh Exp $ * * device driver for philips saa7134 based TV cards * video4linux video interface @@ -31,8 +31,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#define V4L2_I2C_CLIENTS 1 - /* ------------------------------------------------------------------ */ static unsigned int video_debug = 0; @@ -158,18 +156,20 @@ static struct saa7134_format formats[] = { .h_stop = 719, \ .video_v_start = 24, \ .video_v_stop = 311, \ - .vbi_v_start = 7, \ - .vbi_v_stop = 22, \ + .vbi_v_start_0 = 7, \ + .vbi_v_stop_0 = 22, \ + .vbi_v_start_1 = 319, \ .src_timing = 4 #define NORM_525_60 \ .h_start = 0, \ .h_stop = 703, \ - .video_v_start = 22, \ - .video_v_stop = 22+239, \ - .vbi_v_start = 10, /* FIXME */ \ - .vbi_v_stop = 21, /* FIXME */ \ - .src_timing = 1 + .video_v_start = 23, \ + .video_v_stop = 262, \ + .vbi_v_start_0 = 10, \ + .vbi_v_stop_0 = 21, \ + .vbi_v_start_1 = 273, \ + .src_timing = 7 static struct saa7134_tvnorm tvnorms[] = { { @@ -274,11 +274,12 @@ static struct saa7134_tvnorm tvnorms[] = { .h_start = 0, .h_stop = 719, - .video_v_start = 22, - .video_v_stop = 22+239, - .vbi_v_start = 10, /* FIXME */ - .vbi_v_stop = 21, /* FIXME */ - .src_timing = 1, + .video_v_start = 23, + .video_v_stop = 262, + .vbi_v_start_0 = 10, + .vbi_v_stop_0 = 21, + .vbi_v_start_1 = 273, + .src_timing = 7, .sync_control = 0x18, .luma_control = 0x40, @@ -335,8 +336,8 @@ static const struct v4l2_queryctrl video_ctrls[] = { .default_value = 0, .type = V4L2_CTRL_TYPE_INTEGER, },{ - .id = V4L2_CID_VFLIP, - .name = "vertical flip", + .id = V4L2_CID_HFLIP, + .name = "Mirror", .minimum = 0, .maximum = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -482,7 +483,7 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) dev->crop_bounds.width = norm->h_stop - norm->h_start +1; dev->crop_defrect.width = norm->h_stop - norm->h_start +1; - dev->crop_bounds.top = (norm->vbi_v_stop+1)*2; + dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2; dev->crop_defrect.top = norm->video_v_start*2; dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624) - dev->crop_bounds.top; @@ -521,22 +522,7 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); -#ifdef V4L2_I2C_CLIENTS saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); -#else - { - /* pass down info to the i2c chips (v4l1) */ - struct video_channel c; - memset(&c,0,sizeof(c)); - c.channel = dev->ctl_input; - c.norm = VIDEO_MODE_PAL; - if (norm->id & V4L2_STD_NTSC) - c.norm = VIDEO_MODE_NTSC; - if (norm->id & V4L2_STD_SECAM) - c.norm = VIDEO_MODE_SECAM; - saa7134_i2c_call_clients(dev,VIDIOCSCHAN,&c); - } -#endif } static void video_mux(struct saa7134_dev *dev, int input) @@ -1064,7 +1050,7 @@ static int get_control(struct saa7134_dev *dev, struct v4l2_control *c) case V4L2_CID_PRIVATE_INVERT: c->value = dev->ctl_invert; break; - case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: c->value = dev->ctl_mirror; break; case V4L2_CID_PRIVATE_Y_EVEN: @@ -1139,7 +1125,7 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh, saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); break; - case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: dev->ctl_mirror = c->value; restart_overlay = 1; break; @@ -1407,9 +1393,9 @@ static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f) f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; f->fmt.vbi.offset = 64 * 4; - f->fmt.vbi.start[0] = norm->vbi_v_start; - f->fmt.vbi.count[0] = norm->vbi_v_stop - norm->vbi_v_start +1; - f->fmt.vbi.start[1] = norm->video_v_stop + norm->vbi_v_start +1; + f->fmt.vbi.start[0] = norm->vbi_v_start_0; + f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; + f->fmt.vbi.start[1] = norm->vbi_v_start_1; f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ @@ -1880,11 +1866,9 @@ static int video_do_ioctl(struct inode *inode, struct file *file, return -EINVAL; down(&dev->lock); dev->ctl_freq = f->frequency; -#ifdef V4L2_I2C_CLIENTS + saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,f); -#else - saa7134_i2c_call_clients(dev,VIDIOCSFREQ,&dev->ctl_freq); -#endif + saa7134_tvaudio_do_scan(dev); up(&dev->lock); return 0; @@ -2139,16 +2123,19 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, t->rangelow = (int)(65*16); t->rangehigh = (int)(108*16); -#ifdef V4L2_I2C_CLIENTS - saa7134_i2c_call_clients(dev,VIDIOC_G_TUNER,t); -#else - { - struct video_tuner vt; - memset(&vt,0,sizeof(vt)); - saa7134_i2c_call_clients(dev,VIDIOCGTUNER,&vt); - t->signal = vt.signal; - } -#endif + saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *t = arg; + + if (0 != t->index) + return -EINVAL; + + saa7134_i2c_call_clients(dev,VIDIOC_S_TUNER,t); + return 0; } case VIDIOC_ENUMINPUT: @@ -2182,7 +2169,6 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOC_S_AUDIO: - case VIDIOC_S_TUNER: case VIDIOC_S_INPUT: case VIDIOC_S_STD: return 0; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index ac90a9853236..d6b1c0d4d0f9 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -1,5 +1,5 @@ /* - * $Id: saa7134.h,v 1.38 2005/03/07 12:01:51 kraxel Exp $ + * $Id: saa7134.h,v 1.41 2005/06/07 18:02:26 nsh Exp $ * * v4l2 device driver for philips saa7134 based TV cards * @@ -21,7 +21,7 @@ */ #include -#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,12) +#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,13) #include #include @@ -91,9 +91,10 @@ struct saa7134_tvnorm { unsigned int h_stop; unsigned int video_v_start; unsigned int video_v_stop; - unsigned int vbi_v_start; - unsigned int vbi_v_stop; + unsigned int vbi_v_start_0; + unsigned int vbi_v_stop_0; unsigned int src_timing; + unsigned int vbi_v_start_1; }; struct saa7134_tvaudio { @@ -167,7 +168,7 @@ struct saa7134_format { #define SAA7134_BOARD_SABRENT_SBTTVFM 42 #define SAA7134_BOARD_ZOLID_XPERT_TV7134 43 #define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44 -#define SAA7134_BOARD_AVERMEDIA_307 45 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_307 45 #define SAA7134_BOARD_AVERMEDIA_CARDBUS 46 #define SAA7134_BOARD_CINERGY400_CARDBUS 47 #define SAA7134_BOARD_CINERGY600_MK3 48 @@ -178,6 +179,10 @@ struct saa7134_format { #define SAA7135_BOARD_ASUSTeK_TVFM7135 53 #define SAA7134_BOARD_FLYTVPLATINUM_FM 54 #define SAA7134_BOARD_FLYDVBTDUO 55 +#define SAA7134_BOARD_AVERMEDIA_307 56 +#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57 +#define SAA7134_BOARD_ADS_INSTANT_TV 58 +#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -241,7 +246,7 @@ struct saa7134_dma; /* saa7134 page table */ struct saa7134_pgtable { unsigned int size; - u32 *cpu; + __le32 *cpu; dma_addr_t dma; }; diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 7e6e6dd966a2..39773633cc3c 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -53,6 +53,7 @@ struct tda9887 { unsigned int config; unsigned int pinnacle_id; unsigned int using_v4l2; + unsigned int radio_mode; }; struct tvnorm { @@ -212,12 +213,22 @@ static struct tvnorm tvnorms[] = { } }; -static struct tvnorm radio = { - .name = "radio", +static struct tvnorm radio_stereo = { + .name = "Radio Stereo", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisOFF | + cAudioGain6 ), + .e = ( cAudioIF_5_5 | + cRadioIF_38_90 ), +}; + +static struct tvnorm radio_mono = { + .name = "Radio Mono", .b = ( cFmRadio | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50 ), + cDeemphasis50), .e = ( cAudioIF_5_5 | cRadioIF_38_90 ), }; @@ -354,7 +365,10 @@ static int tda9887_set_tvnorm(struct tda9887 *t, char *buf) int i; if (t->radio) { - norm = &radio; + if (t->radio_mode == V4L2_TUNER_MODE_MONO) + norm = &radio_mono; + else + norm = &radio_stereo; } else { for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { if (tvnorms[i].std & t->std) { @@ -545,11 +559,14 @@ static int tda9887_configure(struct tda9887 *t) memset(buf,0,sizeof(buf)); tda9887_set_tvnorm(t,buf); + buf[1] |= cOutputPort1Inactive; buf[1] |= cOutputPort2Inactive; + if (UNSET != t->pinnacle_id) { tda9887_set_pinnacle(t,buf); } + tda9887_set_config(t,buf); tda9887_set_insmod(t,buf); @@ -592,9 +609,12 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL))) return -ENOMEM; memset(t,0,sizeof(*t)); + t->client = client_template; t->std = 0; t->pinnacle_id = UNSET; + t->radio_mode = V4L2_TUNER_MODE_STEREO; + i2c_set_clientdata(&t->client, t); i2c_attach_client(&t->client); @@ -733,6 +753,16 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) } break; } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner* tuner = arg; + + if (t->radio) { + t->radio_mode = tuner->audmode; + tda9887_configure (t); + } + break; + } default: /* nothing */ break; @@ -740,7 +770,7 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int tda9887_suspend(struct device * dev, pm_message_t state, u32 level) +static int tda9887_suspend(struct device * dev, u32 state, u32 level) { dprintk("tda9887: suspend\n"); return 0; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 81882ddab859..eaabfc858703 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1,5 +1,5 @@ /* - * $Id: tuner-core.c,v 1.5 2005/02/15 15:59:35 kraxel Exp $ + * $Id: tuner-core.c,v 1.15 2005/06/12 01:36:14 mchehab Exp $ * * i2c tv tuner chip device driver * core core, i.e. kernel interfaces, registering and so on @@ -23,6 +23,11 @@ #include #include +/* + * comment line bellow to return to old behavor, where only one I2C device is supported + */ +#define CONFIG_TUNER_MULTI_I2C /**/ + #define UNSET (-1U) /* standard i2c insmod options */ @@ -53,13 +58,16 @@ MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); MODULE_LICENSE("GPL"); static int this_adap; +#ifdef CONFIG_TUNER_MULTI_I2C +static unsigned short first_tuner, tv_tuner, radio_tuner; +#endif static struct i2c_driver driver; static struct i2c_client client_template; /* ---------------------------------------------------------------------- */ -// Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz +/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ static void set_tv_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); @@ -73,14 +81,26 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) return; } if (freq < tv_range[0]*16 || freq > tv_range[1]*16) { - /* FIXME: better do that chip-specific, but - right now we don't have that in the config - struct and this way is still better than no - check at all */ - tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n", - freq/16,freq%16*100/16,tv_range[0],tv_range[1]); - return; + + if (freq >= tv_range[0]*16364 && freq <= tv_range[1]*16384) { + /* V4L2_TUNER_CAP_LOW frequency */ + + tuner_dbg("V4L2_TUNER_CAP_LOW freq selected for TV. Tuners yet doesn't support converting it to valid freq.\n"); + + t->tv_freq(c,freq>>10); + + return; + } else { + /* FIXME: better do that chip-specific, but + right now we don't have that in the config + struct and this way is still better than no + check at all */ + tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n", + freq/16,freq%16*100/16,tv_range[0],tv_range[1]); + return; + } } + tuner_dbg("62.5 Khz freq step selected for TV.\n"); t->tv_freq(c,freq); } @@ -97,11 +117,29 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) return; } if (freq < radio_range[0]*16 || freq > radio_range[1]*16) { - tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n", + if (freq >= tv_range[0]*16364 && freq <= tv_range[1]*16384) { + /* V4L2_TUNER_CAP_LOW frequency */ + if (t->type == TUNER_TEA5767) { + tuner_info("radio freq step 62.5Hz (%d.%06d)\n",(freq>>14),freq%(1<<14)*10000); + t->radio_freq(c,freq>>10); + return; + } + + tuner_dbg("V4L2_TUNER_CAP_LOW freq selected for Radio. Tuners yet doesn't support converting it to valid freq.\n"); + + tuner_info("radio freq (%d.%06d)\n",(freq>>14),freq%(1<<14)*10000); + + t->radio_freq(c,freq>>10); + return; + + } else { + tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n", freq/16,freq%16*100/16, - radio_range[0],radio_range[1]); - return; + radio_range[0],radio_range[1]); + return; + } } + tuner_dbg("62.5 Khz freq step selected for Radio.\n"); t->radio_freq(c,freq); } @@ -129,8 +167,9 @@ static void set_type(struct i2c_client *c, unsigned int type) { struct tuner *t = i2c_get_clientdata(c); + tuner_dbg ("I2C addr 0x%02x with type %d\n",c->addr<<1,type); /* sanity check */ - if (type == UNSET || type == TUNER_ABSENT) + if (type == UNSET || type == TUNER_ABSENT) return; if (type >= tuner_count) return; @@ -145,6 +184,7 @@ static void set_type(struct i2c_client *c, unsigned int type) return; t->initialized = 1; + t->type = type; switch (t->type) { case TUNER_MT2032: @@ -159,6 +199,53 @@ static void set_type(struct i2c_client *c, unsigned int type) } } +#ifdef CONFIG_TUNER_MULTI_I2C +#define CHECK_ADDR(tp,cmd,tun) if (client->addr!=tp) { \ + return 0; } else \ + tuner_info ("Cmd %s accepted to "tun"\n",cmd); +#define CHECK_MODE(cmd) if (t->mode == V4L2_TUNER_RADIO) { \ + CHECK_ADDR(radio_tuner,cmd,"radio") } else \ + { CHECK_ADDR(tv_tuner,cmd,"TV"); } +#else +#define CHECK_ADDR(tp,cmd,tun) tuner_info ("Cmd %s accepted to "tun"\n",cmd); +#define CHECK_MODE(cmd) tuner_info ("Cmd %s accepted\n",cmd); +#endif + +#ifdef CONFIG_TUNER_MULTI_I2C + +static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr) +{ + /* ADDR_UNSET defaults to first available tuner */ + if ( tun_addr->addr == ADDR_UNSET ) { + if (first_tuner != c->addr) + return; + switch (tun_addr->v4l2_tuner) { + case V4L2_TUNER_RADIO: + radio_tuner=c->addr; + break; + default: + tv_tuner=c->addr; + break; + } + } else { + /* Sets tuner to its configured value */ + switch (tun_addr->v4l2_tuner) { + case V4L2_TUNER_RADIO: + radio_tuner=tun_addr->addr; + if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type); + return; + default: + tv_tuner=tun_addr->addr; + if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type); + return; + } + } + set_type(c,tun_addr->type); +} +#else +#define set_addr(c,tun_addr) set_type(c,(tun_addr)->type) +#endif + static char pal[] = "-"; module_param_string(pal, pal, sizeof(pal), 0644); @@ -197,8 +284,17 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) { struct tuner *t; +#ifndef CONFIG_TUNER_MULTI_I2C if (this_adap > 0) return -1; +#else + /* by default, first I2C card is both tv and radio tuner */ + if (this_adap == 0) { + first_tuner = addr; + tv_tuner = addr; + radio_tuner = addr; + } +#endif this_adap++; client_template.adapter = adap; @@ -211,11 +307,12 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) memcpy(&t->i2c,&client_template,sizeof(struct i2c_client)); i2c_set_clientdata(&t->i2c, t); t->type = UNSET; - t->radio_if2 = 10700*1000; // 10.7MHz - FM radio + t->radio_if2 = 10700*1000; /* 10.7MHz - FM radio */ i2c_attach_client(&t->i2c); tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); + set_type(&t->i2c, t->type); return 0; } @@ -228,6 +325,12 @@ static int tuner_probe(struct i2c_adapter *adap) } this_adap = 0; +#ifdef CONFIG_TUNER_MULTI_I2C + first_tuner = 0; + tv_tuner = 0; + radio_tuner = 0; +#endif + if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tuner_attach); return 0; @@ -236,8 +339,14 @@ static int tuner_probe(struct i2c_adapter *adap) static int tuner_detach(struct i2c_client *client) { struct tuner *t = i2c_get_clientdata(client); + int err; + + err=i2c_detach_client(&t->i2c); + if (err) { + tuner_warn ("Client deregistration failed, client not detached.\n"); + return err; + } - i2c_detach_client(&t->i2c); kfree(t); return 0; } @@ -256,18 +365,23 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) unsigned int *iarg = (int*)arg; switch (cmd) { - /* --- configuration --- */ case TUNER_SET_TYPE: set_type(client,*iarg); break; + case TUNER_SET_TYPE_ADDR: + set_addr(client,(struct tuner_addr *)arg); + break; case AUDC_SET_RADIO: + t->mode = V4L2_TUNER_RADIO; + CHECK_ADDR(tv_tuner,"AUDC_SET_RADIO","TV"); + if (V4L2_TUNER_RADIO != t->mode) { set_tv_freq(client,400 * 16); - t->mode = V4L2_TUNER_RADIO; } break; case AUDC_CONFIG_PINNACLE: + CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE","TV"); switch (*iarg) { case 2: tuner_dbg("pinnacle pal\n"); @@ -297,6 +411,8 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) CHECK_V4L2; t->mode = V4L2_TUNER_ANALOG_TV; + CHECK_ADDR(tv_tuner,"VIDIOCSCHAN","TV"); + if (vc->norm < ARRAY_SIZE(map)) t->std = map[vc->norm]; tuner_fixup_std(t); @@ -308,6 +424,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { unsigned long *v = arg; + CHECK_MODE("VIDIOCSFREQ"); CHECK_V4L2; set_freq(client,*v); return 0; @@ -316,15 +433,27 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct video_tuner *vt = arg; + CHECK_ADDR(radio_tuner,"VIDIOCGTUNER","radio"); CHECK_V4L2; - if (V4L2_TUNER_RADIO == t->mode && t->has_signal) - vt->signal = t->has_signal(client); + if (V4L2_TUNER_RADIO == t->mode) { + if (t->has_signal) + vt->signal = t->has_signal(client); + if (t->is_stereo) { + if (t->is_stereo(client)) + vt-> flags |= VIDEO_TUNER_STEREO_ON; + else + vt-> flags &= 0xffff ^ VIDEO_TUNER_STEREO_ON; + } + vt->flags |= V4L2_TUNER_CAP_LOW; /* Allow freqs at 62.5 Hz */ + } + return 0; } case VIDIOCGAUDIO: { struct video_audio *va = arg; + CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO","radio"); CHECK_V4L2; if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) va->mode = t->is_stereo(client) @@ -339,6 +468,8 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) SWITCH_V4L2; t->mode = V4L2_TUNER_ANALOG_TV; + CHECK_ADDR(tv_tuner,"VIDIOC_S_STD","TV"); + t->std = *id; tuner_fixup_std(t); if (t->freq) @@ -349,6 +480,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_frequency *f = arg; + CHECK_MODE("VIDIOC_S_FREQUENCY"); SWITCH_V4L2; if (V4L2_TUNER_RADIO == f->type && V4L2_TUNER_RADIO != t->mode) @@ -361,6 +493,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_frequency *f = arg; + CHECK_MODE("VIDIOC_G_FREQUENCY"); SWITCH_V4L2; f->type = t->mode; f->frequency = t->freq; @@ -370,14 +503,29 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_tuner *tuner = arg; + CHECK_MODE("VIDIOC_G_TUNER"); SWITCH_V4L2; - if (V4L2_TUNER_RADIO == t->mode && t->has_signal) - tuner->signal = t->has_signal(client); + if (V4L2_TUNER_RADIO == t->mode) { + if (t->has_signal) + tuner -> signal = t->has_signal(client); + if (t->is_stereo) { + if (t->is_stereo(client)) { + tuner -> capability |= V4L2_TUNER_CAP_STEREO; + tuner -> rxsubchans |= V4L2_TUNER_SUB_STEREO; + } else { + tuner -> rxsubchans &= 0xffff ^ V4L2_TUNER_SUB_STEREO; + } + } + } + /* Wow to deal with V4L2_TUNER_CAP_LOW ? For now, it accepts from low at 62.5KHz step to high at 62.5 Hz */ tuner->rangelow = tv_range[0] * 16; - tuner->rangehigh = tv_range[1] * 16; +// tuner->rangehigh = tv_range[1] * 16; +// tuner->rangelow = tv_range[0] * 16384; + tuner->rangehigh = tv_range[1] * 16384; break; } default: + tuner_dbg ("Unimplemented IOCTL 0x%08x called to tuner.\n", cmd); /* nothing */ break; } @@ -385,7 +533,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int tuner_suspend(struct device * dev, pm_message_t state, u32 level) +static int tuner_suspend(struct device * dev, u32 state, u32 level) { struct i2c_client *c = container_of(dev, struct i2c_client, dev); struct tuner *t = i2c_get_clientdata(c); diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index 48c6ceff1dc2..539f30557317 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -1,5 +1,5 @@ /* - * $Id: tuner-simple.c,v 1.10 2005/03/08 08:38:00 kraxel Exp $ + * $Id: tuner-simple.c,v 1.21 2005/06/10 19:53:26 nsh Exp $ * * i2c tv tuner chip device driver * controls all those simple 4-control-bytes style tuners. @@ -212,7 +212,25 @@ static struct tunertype tuners[] = { { "Philips FQ1236A MK4", Philips, NTSC, 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 }, + /* Should work for TVF8531MF, TVF8831MF, TVF8731MF */ + { "Ymec TVision TVF-8531MF", Philips, NTSC, + 16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732}, + { "Ymec TVision TVF-5533MF", Philips, NTSC, + 16*160.00,16*454.00,0x01,0x02,0x04,0x8e,732}, + + { "Thomson DDT 7611 (ATSC/NTSC)", THOMSON, ATSC, + 16*157.25,16*454.00,0x39,0x3a,0x3c,0x8e,732}, + { "Tena TNF9533-D/IF", LGINNOTEK, PAL, + 16*160.25, 16*464.25, 0x01,0x02,0x08,0x8e,623}, + + /* + * This entry is for TEA5767 FM radio only chip used on several boards + * w/TV tuner + */ + { TEA5767_TUNER_NAME, Philips, RADIO, + -1, -1, 0, 0, 0, TEA5767_LOW_LO_32768,0}, }; + unsigned const int tuner_count = ARRAY_SIZE(tuners); /* ---------------------------------------------------------------------- */ @@ -223,6 +241,7 @@ static int tuner_getstatus(struct i2c_client *c) if (1 != i2c_master_recv(c,&byte,1)) return 0; + return byte; } @@ -231,17 +250,33 @@ static int tuner_getstatus(struct i2c_client *c) #define TUNER_MODE 0x38 #define TUNER_AFC 0x07 -#define TUNER_STEREO 0x10 /* radio mode */ -#define TUNER_SIGNAL 0x07 /* radio mode */ +#define TUNER_STEREO 0x10 /* radio mode */ +#define TUNER_STEREO_MK3 0x04 /* radio mode */ +#define TUNER_SIGNAL 0x07 /* radio mode */ static int tuner_signal(struct i2c_client *c) { - return (tuner_getstatus(c) & TUNER_SIGNAL)<<13; + return (tuner_getstatus(c) & TUNER_SIGNAL) << 13; } static int tuner_stereo(struct i2c_client *c) { - return (tuner_getstatus (c) & TUNER_STEREO); + int stereo, status; + struct tuner *t = i2c_get_clientdata(c); + + status = tuner_getstatus (c); + + switch (t->type) { + case TUNER_PHILIPS_FM1216ME_MK3: + case TUNER_PHILIPS_FM1236_MK3: + case TUNER_PHILIPS_FM1256_IH3: + stereo = ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); + break; + default: + stereo = status & TUNER_STEREO; + } + + return stereo; } #if 0 /* unused */ @@ -424,6 +459,14 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) buffer[2] = tun->config; switch (t->type) { + case TUNER_TENA_9533_DI: + case TUNER_YMEC_TVF_5533MF: + + /*These values are empirically determinated */ + div = (freq*122)/16 - 20; + buffer[2] = 0x88; /* could be also 0x80 */ + buffer[3] = 0x19; /* could be also 0x10, 0x18, 0x99 */ + break; case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: buffer[3] = 0x19; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 41b635e0d3c6..9a493bea76d8 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -285,7 +285,6 @@ static int chip_thread(void *data) schedule(); } remove_wait_queue(&chip->wq, &wait); - try_to_freeze(PF_FREEZE); if (chip->done || signal_pending(current)) break; dprintk("%s: thread wakeup\n", i2c_clientname(&chip->c)); @@ -1237,17 +1236,17 @@ static int ta8874z_checkit(struct CHIPSTATE *chip) /* audio chip descriptions - struct CHIPDESC */ /* insmod options to enable/disable individual audio chips */ -int tda8425 = 1; -int tda9840 = 1; -int tda9850 = 1; -int tda9855 = 1; -int tda9873 = 1; -int tda9874a = 1; -int tea6300 = 0; // address clash with msp34xx -int tea6320 = 0; // address clash with msp34xx -int tea6420 = 1; -int pic16c54 = 1; -int ta8874z = 0; // address clash with tda9840 +static int tda8425 = 1; +static int tda9840 = 1; +static int tda9850 = 1; +static int tda9855 = 1; +static int tda9873 = 1; +static int tda9874a = 1; +static int tea6300 = 0; // address clash with msp34xx +static int tea6320 = 0; // address clash with msp34xx +static int tea6420 = 1; +static int pic16c54 = 1; +static int ta8874z = 0; // address clash with tda9840 module_param(tda8425, int, 0444); module_param(tda9840, int, 0444); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 3d216973798c..0f03c25489f1 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -75,7 +75,7 @@ hauppauge_tuner_fmt[] = { 0x00000007, "PAL(B/G)" }, { 0x00001000, "NTSC(M)" }, { 0x00000010, "PAL(I)" }, - { 0x00400000, "SECAM(L/L�)" }, + { 0x00400000, "SECAM(L/L´)" }, { 0x00000e00, "PAL(D/K)" }, { 0x03000000, "ATSC Digital" }, }; diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index eafd7061b310..51b99cdbf29e 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -1,3 +1,7 @@ +/* + * $Id: tvmixer.c,v 1.8 2005/06/12 04:19:19 mchehab Exp $ + */ + #include #include #include diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index b0d4bcb027d0..70ecbdb80277 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -1,4 +1,6 @@ /* + * $Id: v4l1-compat.c,v 1.9 2005/06/12 04:19:19 mchehab Exp $ + * * Video for Linux Two * Backward Compatibility Layer * @@ -15,14 +17,11 @@ * */ -#ifndef __KERNEL__ -#define __KERNEL__ -#endif - #include #include #include +#include #include #include #include @@ -787,12 +786,15 @@ v4l_compat_translate_ioctl(struct inode *inode, !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) aud->step = qctrl2.step; aud->mode = 0; + + memset(&tun2,0,sizeof(tun2)); err = drv(inode, file, VIDIOC_G_TUNER, &tun2); if (err < 0) { dprintk("VIDIOCGAUDIO / VIDIOC_G_TUNER: %d\n",err); err = 0; break; } + if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2) aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) diff --git a/drivers/message/i2o/Kconfig b/drivers/message/i2o/Kconfig index 8d132b0d6b12..06e8eb19a05c 100644 --- a/drivers/message/i2o/Kconfig +++ b/drivers/message/i2o/Kconfig @@ -24,10 +24,28 @@ config I2O If unsure, say N. +config I2O_EXT_ADAPTEC + bool "Enable Adaptec extensions" + depends on I2O + default y + ---help--- + Say Y for support of raidutils for Adaptec I2O controllers. You also + have to say Y to "I2O Configuration support", "I2O SCSI OSM" below + and to "SCSI generic support" under "SCSI device configuration". + +config I2O_EXT_ADAPTEC_DMA64 + bool "Enable 64-bit DMA" + depends on I2O_EXT_ADAPTEC && ( 64BIT || HIGHMEM64G ) + default y + ---help--- + Say Y for support of 64-bit DMA transfer mode on Adaptec I2O + controllers. + Note: You need at least firmware version 3709. + config I2O_CONFIG tristate "I2O Configuration support" - depends on PCI && I2O - help + depends on I2O + ---help--- Say Y for support of the configuration interface for the I2O adapters. If you have a RAID controller from Adaptec and you want to use the raidutils to manage your RAID array, you have to say Y here. @@ -35,10 +53,28 @@ config I2O_CONFIG To compile this support as a module, choose M here: the module will be called i2o_config. +config I2O_CONFIG_OLD_IOCTL + bool "Enable ioctls (OBSOLETE)" + depends on I2O_CONFIG + default y + ---help--- + Enables old ioctls. + +config I2O_BUS + tristate "I2O Bus Adapter OSM" + depends on I2O + ---help--- + Include support for the I2O Bus Adapter OSM. The Bus Adapter OSM + provides access to the busses on the I2O controller. The main purpose + is to rescan the bus to find new devices. + + To compile this support as a module, choose M here: the + module will be called i2o_bus. + config I2O_BLOCK tristate "I2O Block OSM" depends on I2O - help + ---help--- Include support for the I2O Block OSM. The Block OSM presents disk and other structured block devices to the operating system. If you are using an RAID controller, you could access the array only by @@ -51,7 +87,7 @@ config I2O_BLOCK config I2O_SCSI tristate "I2O SCSI OSM" depends on I2O && SCSI - help + ---help--- Allows direct SCSI access to SCSI devices on a SCSI or FibreChannel I2O controller. You can use both the SCSI and Block OSM together if you wish. To access a RAID array, you must use the Block OSM driver. @@ -63,7 +99,7 @@ config I2O_SCSI config I2O_PROC tristate "I2O /proc support" depends on I2O - help + ---help--- If you say Y here and to "/proc file system support", you will be able to read I2O related information from the virtual directory /proc/i2o. diff --git a/drivers/message/i2o/Makefile b/drivers/message/i2o/Makefile index aabc6cdc3fce..2c2e39aa1efa 100644 --- a/drivers/message/i2o/Makefile +++ b/drivers/message/i2o/Makefile @@ -6,8 +6,11 @@ # i2o_core-y += iop.o driver.o device.o debug.o pci.o exec-osm.o +i2o_bus-y += bus-osm.o +i2o_config-y += config-osm.o obj-$(CONFIG_I2O) += i2o_core.o obj-$(CONFIG_I2O_CONFIG)+= i2o_config.o +obj-$(CONFIG_I2O_BUS) += i2o_bus.o obj-$(CONFIG_I2O_BLOCK) += i2o_block.o obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o obj-$(CONFIG_I2O_PROC) += i2o_proc.o diff --git a/drivers/message/i2o/bus-osm.c b/drivers/message/i2o/bus-osm.c new file mode 100644 index 000000000000..151b228e1cb3 --- /dev/null +++ b/drivers/message/i2o/bus-osm.c @@ -0,0 +1,164 @@ +/* + * Bus Adapter OSM + * + * Copyright (C) 2005 Markus Lidel + * + * 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. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +#include +#include + +#define OSM_NAME "bus-osm" +#define OSM_VERSION "$Rev$" +#define OSM_DESCRIPTION "I2O Bus Adapter OSM" + +static struct i2o_driver i2o_bus_driver; + +/* Bus OSM class handling definition */ +static struct i2o_class_id i2o_bus_class_id[] = { + {I2O_CLASS_BUS_ADAPTER}, + {I2O_CLASS_END} +}; + +/** + * i2o_bus_scan - Scan the bus for new devices + * @dev: I2O device of the bus, which should be scanned + * + * Scans the bus dev for new / removed devices. After the scan a new LCT + * will be fetched automatically. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_bus_scan(struct i2o_device *dev) +{ + struct i2o_message __iomem *msg; + u32 m; + + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_BUS_SCAN << 24 | HOST_TID << 12 | dev->lct_data.tid, + &msg->u.head[1]); + + return i2o_msg_post_wait(dev->iop, m, 60); +}; + +/** + * i2o_bus_store_scan - Scan the I2O Bus Adapter + * @d: device which should be scanned + * + * Returns count. + */ +static ssize_t i2o_bus_store_scan(struct device *d, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2o_device *i2o_dev = to_i2o_device(d); + int rc; + + if ((rc = i2o_bus_scan(i2o_dev))) + osm_warn("bus scan failed %d\n", rc); + + return count; +} + +/* Bus Adapter OSM device attributes */ +static DEVICE_ATTR(scan, S_IWUSR, NULL, i2o_bus_store_scan); + +/** + * i2o_bus_probe - verify if dev is a I2O Bus Adapter device and install it + * @dev: device to verify if it is a I2O Bus Adapter device + * + * Because we want all Bus Adapters always return 0. + * + * Returns 0. + */ +static int i2o_bus_probe(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(get_device(dev)); + + device_create_file(dev, &dev_attr_scan); + + osm_info("device added (TID: %03x)\n", i2o_dev->lct_data.tid); + + return 0; +}; + +/** + * i2o_bus_remove - remove the I2O Bus Adapter device from the system again + * @dev: I2O Bus Adapter device which should be removed + * + * Always returns 0. + */ +static int i2o_bus_remove(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + + device_remove_file(dev, &dev_attr_scan); + + put_device(dev); + + osm_info("device removed (TID: %03x)\n", i2o_dev->lct_data.tid); + + return 0; +}; + +/* Bus Adapter OSM driver struct */ +static struct i2o_driver i2o_bus_driver = { + .name = OSM_NAME, + .classes = i2o_bus_class_id, + .driver = { + .probe = i2o_bus_probe, + .remove = i2o_bus_remove, + }, +}; + +/** + * i2o_bus_init - Bus Adapter OSM initialization function + * + * Only register the Bus Adapter OSM in the I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_bus_init(void) +{ + int rc; + + printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n"); + + /* Register Bus Adapter OSM into I2O core */ + rc = i2o_driver_register(&i2o_bus_driver); + if (rc) { + osm_err("Could not register Bus Adapter OSM\n"); + return rc; + } + + return 0; +}; + +/** + * i2o_bus_exit - Bus Adapter OSM exit function + * + * Unregisters Bus Adapter OSM from I2O core. + */ +static void __exit i2o_bus_exit(void) +{ + i2o_driver_unregister(&i2o_bus_driver); +}; + +MODULE_AUTHOR("Markus Lidel "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(OSM_DESCRIPTION); +MODULE_VERSION(OSM_VERSION); + +module_init(i2o_bus_init); +module_exit(i2o_bus_exit); diff --git a/drivers/message/i2o/config-osm.c b/drivers/message/i2o/config-osm.c new file mode 100644 index 000000000000..d0267609a949 --- /dev/null +++ b/drivers/message/i2o/config-osm.c @@ -0,0 +1,579 @@ +/* + * Configuration OSM + * + * Copyright (C) 2005 Markus Lidel + * + * 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. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +#include +#include +#include + +#include + +#define OSM_NAME "config-osm" +#define OSM_VERSION "1.248" +#define OSM_DESCRIPTION "I2O Configuration OSM" + +/* access mode user rw */ +#define S_IWRSR (S_IRUSR | S_IWUSR) + +static struct i2o_driver i2o_config_driver; + +/* Special file operations for sysfs */ +struct fops_attribute { + struct bin_attribute bin; + struct file_operations fops; +}; + +/** + * sysfs_read_dummy + */ +static ssize_t sysfs_read_dummy(struct kobject *kobj, char *buf, loff_t offset, + size_t count) +{ + return 0; +}; + +/** + * sysfs_write_dummy + */ +static ssize_t sysfs_write_dummy(struct kobject *kobj, char *buf, loff_t offset, + size_t count) +{ + return 0; +}; + +/** + * sysfs_create_fops_file - Creates attribute with special file operations + * @kobj: kobject which should contains the attribute + * @attr: attributes which should be used to create file + * + * First creates attribute @attr in kobject @kobj. If it is the first time + * this function is called, merge old fops from sysfs with new one and + * write it back. Afterwords the new fops will be set for the created + * attribute. + * + * Returns 0 on success or negative error code on failure. + */ +static int sysfs_create_fops_file(struct kobject *kobj, + struct fops_attribute *attr) +{ + struct file_operations tmp, *fops; + struct dentry *d; + struct qstr qstr; + int rc; + + fops = &attr->fops; + + if (fops->read) + attr->bin.read = sysfs_read_dummy; + + if (fops->write) + attr->bin.write = sysfs_write_dummy; + + if ((rc = sysfs_create_bin_file(kobj, &attr->bin))) + return rc; + + qstr.name = attr->bin.attr.name; + qstr.len = strlen(qstr.name); + qstr.hash = full_name_hash(qstr.name, qstr.len); + + if ((d = lookup_hash(&qstr, kobj->dentry))) { + if (!fops->owner) { + memcpy(&tmp, d->d_inode->i_fop, sizeof(tmp)); + if (fops->read) + tmp.read = fops->read; + if (fops->write) + tmp.write = fops->write; + memcpy(fops, &tmp, sizeof(tmp)); + } + + d->d_inode->i_fop = fops; + } else + sysfs_remove_bin_file(kobj, &attr->bin); + + return -ENOENT; +}; + +/** + * sysfs_remove_fops_file - Remove attribute with special file operations + * @kobj: kobject which contains the attribute + * @attr: attributes which are used to create file + * + * Only wrapper arround sysfs_remove_bin_file() + * + * Returns 0 on success or negative error code on failure. + */ +static inline int sysfs_remove_fops_file(struct kobject *kobj, + struct fops_attribute *attr) +{ + return sysfs_remove_bin_file(kobj, &attr->bin); +}; + +/** + * i2o_config_read_hrt - Returns the HRT of the controller + * @kob: kernel object handle + * @buf: buffer into which the HRT should be copied + * @off: file offset + * @count: number of bytes to read + * + * Put @count bytes starting at @off into @buf from the HRT of the I2O + * controller corresponding to @kobj. + * + * Returns number of bytes copied into buffer. + */ +static ssize_t i2o_config_read_hrt(struct kobject *kobj, char *buf, + loff_t offset, size_t count) +{ + struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; + i2o_hrt *hrt = c->hrt.virt; + + u32 size = (hrt->num_entries * hrt->entry_len + 2) * 4; + + if (offset > size) + return 0; + + if (offset + count > size) + count = size - offset; + + memcpy(buf, (u8 *) hrt + offset, count); + + return count; +}; + +/** + * i2o_config_read_lct - Returns the LCT of the controller + * @kob: kernel object handle + * @buf: buffer into which the LCT should be copied + * @off: file offset + * @count: number of bytes to read + * + * Put @count bytes starting at @off into @buf from the LCT of the I2O + * controller corresponding to @kobj. + * + * Returns number of bytes copied into buffer. + */ +static ssize_t i2o_config_read_lct(struct kobject *kobj, char *buf, + loff_t offset, size_t count) +{ + struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; + u32 size = c->lct->table_size * 4; + + if (offset > size) + return 0; + + if (offset + count > size) + count = size - offset; + + memcpy(buf, (u8 *) c->lct + offset, count); + + return count; +}; + +#define I2O_CONFIG_SW_ATTR(_name,_mode,_type,_swid) \ +static ssize_t i2o_config_##_name##_read(struct file *file, char __user *buf, size_t count, loff_t * offset) { \ + return i2o_config_sw_read(file, buf, count, offset, _type, _swid); \ +};\ +\ +static ssize_t i2o_config_##_name##_write(struct file *file, const char __user *buf, size_t count, loff_t * offset) { \ + return i2o_config_sw_write(file, buf, count, offset, _type, _swid); \ +}; \ +\ +static struct fops_attribute i2o_config_attr_##_name = { \ + .bin = { .attr = { .name = __stringify(_name), .mode = _mode, \ + .owner = THIS_MODULE }, \ + .size = 0, }, \ + .fops = { .write = i2o_config_##_name##_write, \ + .read = i2o_config_##_name##_read} \ +}; + +#ifdef CONFIG_I2O_EXT_ADAPTEC + +/** + * i2o_config_dpt_reagion - Converts type and id to flash region + * @swtype: type of software module reading + * @swid: id of software which should be read + * + * Converts type and id from I2O spec to the matching region for DPT / + * Adaptec controllers. + * + * Returns region which match type and id or -1 on error. + */ +static u32 i2o_config_dpt_region(u8 swtype, u8 swid) +{ + switch (swtype) { + case I2O_SOFTWARE_MODULE_IRTOS: + /* + * content: operation firmware + * region size: + * 0xbc000 for 2554, 3754, 2564, 3757 + * 0x170000 for 2865 + * 0x17c000 for 3966 + */ + if (!swid) + return 0; + + break; + + case I2O_SOFTWARE_MODULE_IOP_PRIVATE: + /* + * content: BIOS and SMOR + * BIOS size: first 0x8000 bytes + * region size: + * 0x40000 for 2554, 3754, 2564, 3757 + * 0x80000 for 2865, 3966 + */ + if (!swid) + return 1; + + break; + + case I2O_SOFTWARE_MODULE_IOP_CONFIG: + switch (swid) { + case 0: + /* + * content: NVRAM defaults + * region size: 0x2000 bytes + */ + return 2; + case 1: + /* + * content: serial number + * region size: 0x2000 bytes + */ + return 3; + } + break; + } + + return -1; +}; + +#endif + +/** + * i2o_config_sw_read - Read a software module from controller + * @file: file pointer + * @buf: buffer into which the data should be copied + * @count: number of bytes to read + * @off: file offset + * @swtype: type of software module reading + * @swid: id of software which should be read + * + * Transfers @count bytes at offset @offset from IOP into buffer using + * type @swtype and id @swid as described in I2O spec. + * + * Returns number of bytes copied into buffer or error code on failure. + */ +static ssize_t i2o_config_sw_read(struct file *file, char __user * buf, + size_t count, loff_t * offset, u8 swtype, + u32 swid) +{ + struct sysfs_dirent *sd = file->f_dentry->d_parent->d_fsdata; + struct kobject *kobj = sd->s_element; + struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; + u32 m, function = I2O_CMD_SW_UPLOAD; + struct i2o_dma buffer; + struct i2o_message __iomem *msg; + u32 __iomem *mptr; + int rc, status; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; + + mptr = &msg->body[3]; + + if ((rc = i2o_dma_alloc(&c->pdev->dev, &buffer, count, GFP_KERNEL))) { + i2o_msg_nop(c, m); + return rc; + } +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + mptr = &msg->body[4]; + function = I2O_CMD_PRIVATE; + + writel(TEN_WORD_MSG_SIZE | SGL_OFFSET_8, &msg->u.head[0]); + + writel(I2O_VENDOR_DPT << 16 | I2O_DPT_FLASH_READ, + &msg->body[0]); + writel(i2o_config_dpt_region(swtype, swid), &msg->body[1]); + writel(*offset, &msg->body[2]); + writel(count, &msg->body[3]); + } else +#endif + writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]); + + writel(0xD0000000 | count, mptr++); + writel(buffer.phys, mptr); + + writel(function << 24 | HOST_TID << 12 | ADAPTER_TID, &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (!c->adaptec) +#endif + { + writel((u32) swtype << 16 | (u32) 1 << 8, &msg->body[0]); + writel(0, &msg->body[1]); + writel(swid, &msg->body[2]); + } + + status = i2o_msg_post_wait_mem(c, m, 60, &buffer); + + if (status == I2O_POST_WAIT_OK) { + if (!(rc = copy_to_user(buf, buffer.virt, count))) { + rc = count; + *offset += count; + } + } else + rc = -EIO; + + if (status != -ETIMEDOUT) + i2o_dma_free(&c->pdev->dev, &buffer); + + return rc; +}; + +/** + * i2o_config_sw_write - Write a software module to controller + * @file: file pointer + * @buf: buffer into which the data should be copied + * @count: number of bytes to read + * @off: file offset + * @swtype: type of software module writing + * @swid: id of software which should be written + * + * Transfers @count bytes at offset @offset from buffer to IOP using + * type @swtype and id @swid as described in I2O spec. + * + * Returns number of bytes copied from buffer or error code on failure. + */ +static ssize_t i2o_config_sw_write(struct file *file, const char __user * buf, + size_t count, loff_t * offset, u8 swtype, + u32 swid) +{ + struct sysfs_dirent *sd = file->f_dentry->d_parent->d_fsdata; + struct kobject *kobj = sd->s_element; + struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; + u32 m, function = I2O_CMD_SW_DOWNLOAD; + struct i2o_dma buffer; + struct i2o_message __iomem *msg; + u32 __iomem *mptr; + int rc, status; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; + + mptr = &msg->body[3]; + + if ((rc = i2o_dma_alloc(&c->pdev->dev, &buffer, count, GFP_KERNEL))) + goto nop_msg; + + if ((rc = copy_from_user(buffer.virt, buf, count))) + goto free_buffer; + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + mptr = &msg->body[4]; + function = I2O_CMD_PRIVATE; + + writel(TEN_WORD_MSG_SIZE | SGL_OFFSET_8, &msg->u.head[0]); + + writel(I2O_VENDOR_DPT << 16 | I2O_DPT_FLASH_WRITE, + &msg->body[0]); + writel(i2o_config_dpt_region(swtype, swid), &msg->body[1]); + writel(*offset, &msg->body[2]); + writel(count, &msg->body[3]); + } else +#endif + writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]); + + writel(0xD4000000 | count, mptr++); + writel(buffer.phys, mptr); + + writel(function << 24 | HOST_TID << 12 | ADAPTER_TID, &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (!c->adaptec) +#endif + { + writel((u32) swtype << 16 | (u32) 1 << 8, &msg->body[0]); + writel(0, &msg->body[1]); + writel(swid, &msg->body[2]); + } + + status = i2o_msg_post_wait_mem(c, m, 60, &buffer); + + if (status != -ETIMEDOUT) + i2o_dma_free(&c->pdev->dev, &buffer); + + if (status != I2O_POST_WAIT_OK) + return -EIO; + + *offset += count; + + return count; + + free_buffer: + i2o_dma_free(&c->pdev->dev, &buffer); + + nop_msg: + i2o_msg_nop(c, m); + + return rc; +}; + +/* attribute for HRT in sysfs */ +static struct bin_attribute i2o_config_hrt_attr = { + .attr = { + .name = "hrt", + .mode = S_IRUGO, + .owner = THIS_MODULE}, + .size = 0, + .read = i2o_config_read_hrt +}; + +/* attribute for LCT in sysfs */ +static struct bin_attribute i2o_config_lct_attr = { + .attr = { + .name = "lct", + .mode = S_IRUGO, + .owner = THIS_MODULE}, + .size = 0, + .read = i2o_config_read_lct +}; + +/* IRTOS firmware access */ +I2O_CONFIG_SW_ATTR(irtos, S_IWRSR, I2O_SOFTWARE_MODULE_IRTOS, 0); + +#ifdef CONFIG_I2O_EXT_ADAPTEC + +/* + * attribute for BIOS / SMOR, nvram and serial number access on DPT / Adaptec + * controllers + */ +I2O_CONFIG_SW_ATTR(bios, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_PRIVATE, 0); +I2O_CONFIG_SW_ATTR(nvram, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_CONFIG, 0); +I2O_CONFIG_SW_ATTR(serial, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_CONFIG, 1); + +#endif + +/** + * i2o_config_notify_controller_add - Notify of added controller + * @c: the controller which was added + * + * If a I2O controller is added, we catch the notification to add sysfs + * entries. + */ +static void i2o_config_notify_controller_add(struct i2o_controller *c) +{ + struct kobject *kobj = &c->exec->device.kobj; + + sysfs_create_bin_file(kobj, &i2o_config_hrt_attr); + sysfs_create_bin_file(kobj, &i2o_config_lct_attr); + + sysfs_create_fops_file(kobj, &i2o_config_attr_irtos); +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + sysfs_create_fops_file(kobj, &i2o_config_attr_bios); + sysfs_create_fops_file(kobj, &i2o_config_attr_nvram); + sysfs_create_fops_file(kobj, &i2o_config_attr_serial); + } +#endif +}; + +/** + * i2o_config_notify_controller_remove - Notify of removed controller + * @c: the controller which was removed + * + * If a I2O controller is removed, we catch the notification to remove the + * sysfs entries. + */ +static void i2o_config_notify_controller_remove(struct i2o_controller *c) +{ + struct kobject *kobj = &c->exec->device.kobj; + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + sysfs_remove_fops_file(kobj, &i2o_config_attr_serial); + sysfs_remove_fops_file(kobj, &i2o_config_attr_nvram); + sysfs_remove_fops_file(kobj, &i2o_config_attr_bios); + } +#endif + sysfs_remove_fops_file(kobj, &i2o_config_attr_irtos); + + sysfs_remove_bin_file(kobj, &i2o_config_lct_attr); + sysfs_remove_bin_file(kobj, &i2o_config_hrt_attr); +}; + +/* Config OSM driver struct */ +static struct i2o_driver i2o_config_driver = { + .name = OSM_NAME, + .notify_controller_add = i2o_config_notify_controller_add, + .notify_controller_remove = i2o_config_notify_controller_remove +}; + +#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL +#include "i2o_config.c" +#endif + +/** + * i2o_config_init - Configuration OSM initialization function + * + * Registers Configuration OSM in the I2O core and if old ioctl's are + * compiled in initialize them. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_config_init(void) +{ + printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n"); + + if (i2o_driver_register(&i2o_config_driver)) { + osm_err("handler register failed.\n"); + return -EBUSY; + } +#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL + if (i2o_config_old_init()) + i2o_driver_unregister(&i2o_config_driver); +#endif + + return 0; +} + +/** + * i2o_config_exit - Configuration OSM exit function + * + * If old ioctl's are compiled in exit remove them and unregisters + * Configuration OSM from I2O core. + */ +static void i2o_config_exit(void) +{ +#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL + i2o_config_old_exit(); +#endif + + i2o_driver_unregister(&i2o_config_driver); +} + +MODULE_AUTHOR("Markus Lidel "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(OSM_DESCRIPTION); +MODULE_VERSION(OSM_VERSION); + +module_init(i2o_config_init); +module_exit(i2o_config_exit); diff --git a/drivers/message/i2o/core.h b/drivers/message/i2o/core.h new file mode 100644 index 000000000000..c5bcfd70f711 --- /dev/null +++ b/drivers/message/i2o/core.h @@ -0,0 +1,58 @@ +/* + * I2O core internal declarations + * + * Copyright (C) 2005 Markus Lidel + * + * 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. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +/* Exec-OSM */ +extern struct bus_type i2o_bus_type; + +extern struct i2o_driver i2o_exec_driver; +extern int i2o_exec_lct_get(struct i2o_controller *); + +extern int __init i2o_exec_init(void); +extern void __exit i2o_exec_exit(void); + +/* driver */ +extern int i2o_driver_dispatch(struct i2o_controller *, u32); + +extern int __init i2o_driver_init(void); +extern void __exit i2o_driver_exit(void); + +/* PCI */ +extern int __init i2o_pci_init(void); +extern void __exit i2o_pci_exit(void); + +/* device */ +extern void i2o_device_remove(struct i2o_device *); +extern int i2o_device_parse_lct(struct i2o_controller *); + +extern int i2o_device_init(void); +extern void i2o_device_exit(void); + +/* IOP */ +extern struct i2o_controller *i2o_iop_alloc(void); +extern void i2o_iop_free(struct i2o_controller *); + +extern int i2o_iop_add(struct i2o_controller *); +extern void i2o_iop_remove(struct i2o_controller *); + +/* config */ +extern int i2o_parm_issue(struct i2o_device *, int, void *, int, void *, int); + +/* control registers relative to c->base */ +#define I2O_IRQ_STATUS 0x30 +#define I2O_IRQ_MASK 0x34 +#define I2O_IN_PORT 0x40 +#define I2O_OUT_PORT 0x44 + +#define I2O_IRQ_OUTBOUND_POST 0x00000008 diff --git a/drivers/message/i2o/debug.c b/drivers/message/i2o/debug.c index 2a5d478fc60e..018ca887ca85 100644 --- a/drivers/message/i2o/debug.c +++ b/drivers/message/i2o/debug.c @@ -4,8 +4,6 @@ #include #include -extern struct i2o_driver **i2o_drivers; -extern unsigned int i2o_max_drivers; static void i2o_report_util_cmd(u8 cmd); static void i2o_report_exec_cmd(u8 cmd); static void i2o_report_fail_status(u8 req_status, u32 * msg); @@ -23,7 +21,6 @@ void i2o_report_status(const char *severity, const char *str, u8 cmd = (msg[1] >> 24) & 0xFF; u8 req_status = (msg[4] >> 24) & 0xFF; u16 detailed_status = msg[4] & 0xFFFF; - //struct i2o_driver *h = i2o_drivers[msg[2] & (i2o_max_drivers-1)]; if (cmd == I2O_CMD_UTIL_EVT_REGISTER) return; // No status in this reply diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c index eb907e87bc7b..21f16ba3ac38 100644 --- a/drivers/message/i2o/device.c +++ b/drivers/message/i2o/device.c @@ -16,9 +16,7 @@ #include #include #include - -/* Exec OSM functions */ -extern struct bus_type i2o_bus_type; +#include "core.h" /** * i2o_device_issue_claim - claim or release a device @@ -282,8 +280,7 @@ int i2o_device_parse_lct(struct i2o_controller *c) down(&c->lct_lock); - if (c->lct) - kfree(c->lct); + kfree(c->lct); lct = c->dlct.virt; @@ -294,12 +291,12 @@ int i2o_device_parse_lct(struct i2o_controller *c) } if (lct->table_size * 4 > c->dlct.len) { - memcpy_fromio(c->lct, c->dlct.virt, c->dlct.len); + memcpy(c->lct, c->dlct.virt, c->dlct.len); up(&c->lct_lock); return -EAGAIN; } - memcpy_fromio(c->lct, c->dlct.virt, lct->table_size * 4); + memcpy(c->lct, c->dlct.virt, lct->table_size * 4); lct = c->lct; @@ -354,7 +351,7 @@ static ssize_t i2o_device_class_show_class_id(struct class_device *cd, { struct i2o_device *dev = to_i2o_device(cd->dev); - sprintf(buf, "%03x\n", dev->lct_data.class_id); + sprintf(buf, "0x%03x\n", dev->lct_data.class_id); return strlen(buf) + 1; }; @@ -369,7 +366,7 @@ static ssize_t i2o_device_class_show_tid(struct class_device *cd, char *buf) { struct i2o_device *dev = to_i2o_device(cd->dev); - sprintf(buf, "%03x\n", dev->lct_data.tid); + sprintf(buf, "0x%03x\n", dev->lct_data.tid); return strlen(buf) + 1; }; @@ -401,25 +398,27 @@ static int i2o_device_class_add(struct class_device *cd) /* create user entries for this device */ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid); - if (tmp) + if (tmp && (tmp != i2o_dev)) sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, "user"); /* create user entries refering to this device */ list_for_each_entry(tmp, &c->devices, list) - if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + if ((tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + && (tmp != i2o_dev)) sysfs_create_link(&tmp->device.kobj, &i2o_dev->device.kobj, "user"); /* create parent entries for this device */ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid); - if (tmp) + if (tmp && (tmp != i2o_dev)) sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, "parent"); /* create parent entries refering to this device */ list_for_each_entry(tmp, &c->devices, list) - if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + if ((tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + && (tmp != i2o_dev)) sysfs_create_link(&tmp->device.kobj, &i2o_dev->device.kobj, "parent"); @@ -444,9 +443,8 @@ static struct class_interface i2o_device_class_interface = { * Note that the minimum sized reslist is 8 bytes and contains * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. */ - int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist, - int oplen, void *reslist, int reslen) + int oplen, void *reslist, int reslen) { struct i2o_message __iomem *msg; u32 m; @@ -489,7 +487,7 @@ int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist, if (rc == -ETIMEDOUT) return rc; - memcpy_fromio(reslist, res.virt, res.len); + memcpy(reslist, res.virt, res.len); i2o_dma_free(dev, &res); /* Query failed */ @@ -531,17 +529,23 @@ int i2o_parm_field_get(struct i2o_device *i2o_dev, 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 */ + u8 *resblk; /* 8 bytes for header */ int size; if (field == -1) /* whole group */ opblk[4] = -1; + resblk = kmalloc(buflen + 8, GFP_KERNEL | GFP_ATOMIC); + if (!resblk) + return -ENOMEM; + size = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_GET, opblk, - sizeof(opblk), resblk, sizeof(resblk)); + sizeof(opblk), resblk, buflen + 8); memcpy(buf, resblk + 8, buflen); /* cut off header */ + kfree(resblk); + if (size > buflen) return buflen; diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c index 91f4edbb2a27..739bfdef0c6d 100644 --- a/drivers/message/i2o/driver.c +++ b/drivers/message/i2o/driver.c @@ -17,9 +17,12 @@ #include #include #include +#include "core.h" + +#define OSM_NAME "i2o" /* max_drivers - Maximum I2O drivers (OSMs) which could be registered */ -unsigned int i2o_max_drivers = I2O_MAX_DRIVERS; +static unsigned int i2o_max_drivers = I2O_MAX_DRIVERS; module_param_named(max_drivers, i2o_max_drivers, uint, 0); MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support"); @@ -76,17 +79,16 @@ int i2o_driver_register(struct i2o_driver *drv) int rc = 0; unsigned long flags; - pr_debug("i2o: Register driver %s\n", drv->name); + osm_debug("Register driver %s\n", drv->name); if (drv->event) { drv->event_queue = create_workqueue(drv->name); if (!drv->event_queue) { - printk(KERN_ERR "i2o: Could not initialize event queue " - "for driver %s\n", drv->name); + osm_err("Could not initialize event queue for driver " + "%s\n", drv->name); return -EFAULT; } - pr_debug("i2o: Event queue initialized for driver %s\n", - drv->name); + osm_debug("Event queue initialized for driver %s\n", drv->name); } else drv->event_queue = NULL; @@ -97,8 +99,8 @@ int i2o_driver_register(struct i2o_driver *drv) for (i = 0; i2o_drivers[i]; i++) if (i >= i2o_max_drivers) { - printk(KERN_ERR "i2o: too many drivers registered, " - "increase max_drivers\n"); + osm_err("too many drivers registered, increase " + "max_drivers\n"); spin_unlock_irqrestore(&i2o_drivers_lock, flags); return -EFAULT; } @@ -108,18 +110,16 @@ int i2o_driver_register(struct i2o_driver *drv) spin_unlock_irqrestore(&i2o_drivers_lock, flags); - pr_debug("i2o: driver %s gets context id %d\n", drv->name, - drv->context); + osm_debug("driver %s gets context id %d\n", drv->name, drv->context); list_for_each_entry(c, &i2o_controllers, list) { struct i2o_device *i2o_dev; i2o_driver_notify_controller_add(drv, c); list_for_each_entry(i2o_dev, &c->devices, list) - i2o_driver_notify_device_add(drv, i2o_dev); + i2o_driver_notify_device_add(drv, i2o_dev); } - rc = driver_register(&drv->driver); if (rc) destroy_workqueue(drv->event_queue); @@ -139,7 +139,7 @@ void i2o_driver_unregister(struct i2o_driver *drv) struct i2o_controller *c; unsigned long flags; - pr_debug("i2o: unregister driver %s\n", drv->name); + osm_debug("unregister driver %s\n", drv->name); driver_unregister(&drv->driver); @@ -159,7 +159,7 @@ void i2o_driver_unregister(struct i2o_driver *drv) if (drv->event_queue) { destroy_workqueue(drv->event_queue); drv->event_queue = NULL; - pr_debug("i2o: event queue removed for %s\n", drv->name); + osm_debug("event queue removed for %s\n", drv->name); } }; @@ -176,68 +176,70 @@ void i2o_driver_unregister(struct i2o_driver *drv) * on success and if the message should be flushed afterwords. Returns * negative error code on failure (the message will be flushed too). */ -int i2o_driver_dispatch(struct i2o_controller *c, u32 m, - struct i2o_message __iomem *msg) +int i2o_driver_dispatch(struct i2o_controller *c, u32 m) { struct i2o_driver *drv; - u32 context = readl(&msg->u.s.icntxt); + struct i2o_message *msg = i2o_msg_out_to_virt(c, m); + u32 context = le32_to_cpu(msg->u.s.icntxt); + unsigned long flags; - if (likely(context < i2o_max_drivers)) { - spin_lock(&i2o_drivers_lock); - drv = i2o_drivers[context]; - spin_unlock(&i2o_drivers_lock); - - if (unlikely(!drv)) { - printk(KERN_WARNING "%s: Spurious reply to unknown " - "driver %d\n", c->name, context); - return -EIO; - } - - if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) { - struct i2o_device *dev, *tmp; - struct i2o_event *evt; - u16 size; - u16 tid; - - tid = readl(&msg->u.head[1]) & 0x1fff; - - pr_debug("%s: event received from device %d\n", c->name, - tid); - - /* cut of header from message size (in 32-bit words) */ - size = (readl(&msg->u.head[0]) >> 16) - 5; - - evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC); - if (!evt) - return -ENOMEM; - memset(evt, 0, size * 4 + sizeof(*evt)); - - evt->size = size; - memcpy_fromio(&evt->tcntxt, &msg->u.s.tcntxt, - (size + 2) * 4); - - list_for_each_entry_safe(dev, tmp, &c->devices, list) - if (dev->lct_data.tid == tid) { - evt->i2o_dev = dev; - break; - } - - INIT_WORK(&evt->work, (void (*)(void *))drv->event, - evt); - queue_work(drv->event_queue, &evt->work); - return 1; - } - - if (likely(drv->reply)) - return drv->reply(c, m, msg); - else - pr_debug("%s: Reply to driver %s, but no reply function" - " defined!\n", c->name, drv->name); + if (unlikely(context >= i2o_max_drivers)) { + osm_warn("%s: Spurious reply to unknown driver %d\n", c->name, + context); return -EIO; - } else - printk(KERN_WARNING "%s: Spurious reply to unknown driver " - "%d\n", c->name, readl(&msg->u.s.icntxt)); - return -EIO; + } + + spin_lock_irqsave(&i2o_drivers_lock, flags); + drv = i2o_drivers[context]; + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + + if (unlikely(!drv)) { + osm_warn("%s: Spurious reply to unknown driver %d\n", c->name, + context); + return -EIO; + } + + if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) { + struct i2o_device *dev, *tmp; + struct i2o_event *evt; + u16 size; + u16 tid = le32_to_cpu(msg->u.head[1]) & 0xfff; + + osm_debug("event received from device %d\n", tid); + + if (!drv->event) + return -EIO; + + /* cut of header from message size (in 32-bit words) */ + size = (le32_to_cpu(msg->u.head[0]) >> 16) - 5; + + evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC | __GFP_ZERO); + if (!evt) + return -ENOMEM; + + evt->size = size; + evt->tcntxt = le32_to_cpu(msg->u.s.tcntxt); + evt->event_indicator = le32_to_cpu(msg->body[0]); + memcpy(&evt->tcntxt, &msg->u.s.tcntxt, size * 4); + + list_for_each_entry_safe(dev, tmp, &c->devices, list) + if (dev->lct_data.tid == tid) { + evt->i2o_dev = dev; + break; + } + + INIT_WORK(&evt->work, (void (*)(void *))drv->event, evt); + queue_work(drv->event_queue, &evt->work); + return 1; + } + + if (unlikely(!drv->reply)) { + osm_debug("%s: Reply to driver %s, but no reply function" + " defined!\n", c->name, drv->name); + return -EIO; + } + + return drv->reply(c, m, msg); } /** @@ -334,11 +336,11 @@ int __init i2o_driver_init(void) if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64) || ((i2o_max_drivers ^ (i2o_max_drivers - 1)) != (2 * i2o_max_drivers - 1))) { - printk(KERN_WARNING "i2o: max_drivers set to %d, but must be " - ">=2 and <= 64 and a power of 2\n", i2o_max_drivers); + osm_warn("max_drivers set to %d, but must be >=2 and <= 64 and " + "a power of 2\n", i2o_max_drivers); i2o_max_drivers = I2O_MAX_DRIVERS; } - printk(KERN_INFO "i2o: max drivers = %d\n", i2o_max_drivers); + osm_info("max drivers = %d\n", i2o_max_drivers); i2o_drivers = kmalloc(i2o_max_drivers * sizeof(*i2o_drivers), GFP_KERNEL); diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c index 79c1cbfb8f44..bda2c62648ba 100644 --- a/drivers/message/i2o/exec-osm.c +++ b/drivers/message/i2o/exec-osm.c @@ -30,6 +30,7 @@ #include #include #include +#include "core.h" #define OSM_NAME "exec-osm" @@ -37,9 +38,6 @@ struct i2o_driver i2o_exec_driver; static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind); -/* Module internal functions from other sources */ -extern int i2o_device_parse_lct(struct i2o_controller *); - /* global wait list for POST WAIT */ static LIST_HEAD(i2o_exec_wait_list); @@ -50,7 +48,7 @@ struct i2o_exec_wait { u32 tcntxt; /* transaction context from reply */ int complete; /* 1 if reply received otherwise 0 */ u32 m; /* message id */ - struct i2o_message __iomem *msg; /* pointer to the reply message */ + struct i2o_message *msg; /* pointer to the reply message */ struct list_head list; /* node in global wait list */ }; @@ -108,7 +106,8 @@ static void i2o_exec_wait_free(struct i2o_exec_wait *wait) * buffer must not be freed. Instead the event completion will free them * for you. In all other cases the buffer are your problem. * - * Returns 0 on success or negative error code on failure. + * Returns 0 on success, negative error code on timeout or positive error + * code from reply. */ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long timeout, struct i2o_dma *dma) @@ -116,7 +115,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long DECLARE_WAIT_QUEUE_HEAD(wq); struct i2o_exec_wait *wait; static u32 tcntxt = 0x80000000; - struct i2o_message __iomem *msg = c->in_queue.virt + m; + struct i2o_message __iomem *msg = i2o_msg_in_to_virt(c, m); int rc = 0; wait = i2o_exec_wait_alloc(); @@ -153,7 +152,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long list_add(&wait->list, &i2o_exec_wait_list); wait_event_interruptible_timeout(wq, wait->complete, - timeout * HZ); + timeout * HZ); wait->wq = NULL; } @@ -161,8 +160,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long barrier(); if (wait->complete) { - if (readl(&wait->msg->body[0]) >> 24) - rc = readl(&wait->msg->body[0]) & 0xff; + rc = le32_to_cpu(wait->msg->body[0]) >> 24; i2o_flush_reply(c, wait->m); i2o_exec_wait_free(wait); } else { @@ -187,6 +185,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long * @c: I2O controller which answers * @m: message id * @msg: pointer to the I2O reply message + * @context: transaction context of request * * This function is called in interrupt context only. If the reply reached * before the timeout, the i2o_exec_wait struct is filled with the message @@ -201,16 +200,12 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long * message must also be given back to the controller. */ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m, - struct i2o_message __iomem *msg) + struct i2o_message *msg, u32 context) { struct i2o_exec_wait *wait, *tmp; - static spinlock_t lock; + unsigned long flags; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; int rc = 1; - u32 context; - - spin_lock_init(&lock); - - context = readl(&msg->u.s.tcntxt); /* * We need to search through the i2o_exec_wait_list to see if the given @@ -219,11 +214,13 @@ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m, * already expired. Not much we can do about that except log it for * debug purposes, increase timeout, and recompile. */ - spin_lock(&lock); + spin_lock_irqsave(&lock, flags); list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) { if (wait->tcntxt == context) { list_del(&wait->list); + spin_unlock_irqrestore(&lock, flags); + wait->m = m; wait->msg = msg; wait->complete = 1; @@ -245,20 +242,62 @@ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m, rc = -1; } - spin_unlock(&lock); - return rc; } } - spin_unlock(&lock); + spin_unlock_irqrestore(&lock, flags); - pr_debug("%s: Bogus reply in POST WAIT (tr-context: %08x)!\n", c->name, + osm_warn("%s: Bogus reply in POST WAIT (tr-context: %08x)!\n", c->name, context); return -1; }; +/** + * i2o_exec_show_vendor_id - Displays Vendor ID of controller + * @d: device of which the Vendor ID should be displayed + * @buf: buffer into which the Vendor ID should be printed + * + * Returns number of bytes printed into buffer. + */ +static ssize_t i2o_exec_show_vendor_id(struct device *d, struct device_attribute *attr, char *buf) +{ + struct i2o_device *dev = to_i2o_device(d); + u16 id; + + if (i2o_parm_field_get(dev, 0x0000, 0, &id, 2)) { + sprintf(buf, "0x%04x", id); + return strlen(buf) + 1; + } + + return 0; +}; + +/** + * i2o_exec_show_product_id - Displays Product ID of controller + * @d: device of which the Product ID should be displayed + * @buf: buffer into which the Product ID should be printed + * + * Returns number of bytes printed into buffer. + */ +static ssize_t i2o_exec_show_product_id(struct device *d, struct device_attribute *attr, char *buf) +{ + struct i2o_device *dev = to_i2o_device(d); + u16 id; + + if (i2o_parm_field_get(dev, 0x0000, 1, &id, 2)) { + sprintf(buf, "0x%04x", id); + return strlen(buf) + 1; + } + + return 0; +}; + +/* Exec-OSM device attributes */ +static DEVICE_ATTR(vendor_id, S_IRUGO, i2o_exec_show_vendor_id, NULL); +static DEVICE_ATTR(product_id, S_IRUGO, i2o_exec_show_product_id, NULL); + /** * i2o_exec_probe - Called if a new I2O device (executive class) appears * @dev: I2O device which should be probed @@ -271,10 +310,16 @@ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m, static int i2o_exec_probe(struct device *dev) { struct i2o_device *i2o_dev = to_i2o_device(dev); + struct i2o_controller *c = i2o_dev->iop; i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff); - i2o_dev->iop->exec = i2o_dev; + c->exec = i2o_dev; + + i2o_exec_lct_notify(c, c->lct->change_ind + 1); + + device_create_file(dev, &dev_attr_vendor_id); + device_create_file(dev, &dev_attr_product_id); return 0; }; @@ -289,6 +334,9 @@ static int i2o_exec_probe(struct device *dev) */ static int i2o_exec_remove(struct device *dev) { + device_remove_file(dev, &dev_attr_product_id); + device_remove_file(dev, &dev_attr_vendor_id); + i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0); return 0; @@ -300,12 +348,16 @@ static int i2o_exec_remove(struct device *dev) * * This function handles asynchronus LCT NOTIFY replies. It parses the * new LCT and if the buffer for the LCT was to small sends a LCT NOTIFY - * again. + * again, otherwise send LCT NOTIFY to get informed on next LCT change. */ static void i2o_exec_lct_modified(struct i2o_controller *c) { - if (i2o_device_parse_lct(c) == -EAGAIN) - i2o_exec_lct_notify(c, 0); + u32 change_ind = 0; + + if (i2o_device_parse_lct(c) != -EAGAIN) + change_ind = c->lct->change_ind + 1; + + i2o_exec_lct_notify(c, change_ind); }; /** @@ -325,8 +377,14 @@ static void i2o_exec_lct_modified(struct i2o_controller *c) static int i2o_exec_reply(struct i2o_controller *c, u32 m, struct i2o_message *msg) { - if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) { // Fail bit is set - struct i2o_message __iomem *pmsg; /* preserved message */ + u32 context; + + if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) { + /* + * If Fail bit is set we must take the transaction context of + * the preserved message to find the right request again. + */ + struct i2o_message __iomem *pmsg; u32 pm; pm = le32_to_cpu(msg->body[3]); @@ -335,15 +393,15 @@ static int i2o_exec_reply(struct i2o_controller *c, u32 m, i2o_report_status(KERN_INFO, "i2o_core", msg); - /* Release the preserved msg by resubmitting it as a NOP */ + context = readl(&pmsg->u.s.tcntxt); + + /* Release the preserved msg */ i2o_msg_nop(c, pm); + } else + context = le32_to_cpu(msg->u.s.tcntxt); - /* If reply to i2o_post_wait failed, return causes a timeout */ - return -1; - } - - if (le32_to_cpu(msg->u.s.tcntxt) & 0x80000000) - return i2o_msg_post_wait_complete(c, m, msg); + if (context & 0x80000000) + return i2o_msg_post_wait_complete(c, m, msg, context); if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) { struct work_struct *work; @@ -381,8 +439,9 @@ static int i2o_exec_reply(struct i2o_controller *c, u32 m, */ static void i2o_exec_event(struct i2o_event *evt) { - osm_info("Event received from device: %d\n", - evt->i2o_dev->lct_data.tid); + if (likely(evt->i2o_dev)) + osm_debug("Event received from device: %d\n", + evt->i2o_dev->lct_data.tid); kfree(evt); }; diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index 4830b7759061..f283b5bafdd3 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -62,7 +62,7 @@ #include "i2o_block.h" #define OSM_NAME "block-osm" -#define OSM_VERSION "$Rev$" +#define OSM_VERSION "1.287" #define OSM_DESCRIPTION "I2O Block Device OSM" static struct i2o_driver i2o_block_driver; @@ -104,7 +104,8 @@ static int i2o_block_remove(struct device *dev) struct i2o_device *i2o_dev = to_i2o_device(dev); struct i2o_block_device *i2o_blk_dev = dev_get_drvdata(dev); - osm_info("Device removed %s\n", i2o_blk_dev->gd->disk_name); + osm_info("device removed (TID: %03x): %s\n", i2o_dev->lct_data.tid, + i2o_blk_dev->gd->disk_name); i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0); @@ -145,6 +146,29 @@ static int i2o_block_device_flush(struct i2o_device *dev) return i2o_msg_post_wait(dev->iop, m, 60); }; +/** + * i2o_block_issue_flush - device-flush interface for block-layer + * @queue: the request queue of the device which should be flushed + * @disk: gendisk + * @error_sector: error offset + * + * Helper function to provide flush functionality to block-layer. + * + * Returns 0 on success or negative error code on failure. + */ + +static int i2o_block_issue_flush(request_queue_t * queue, struct gendisk *disk, + sector_t * error_sector) +{ + struct i2o_block_device *i2o_blk_dev = queue->queuedata; + int rc = -ENODEV; + + if (likely(i2o_blk_dev)) + rc = i2o_block_device_flush(i2o_blk_dev->i2o_dev); + + return rc; +} + /** * i2o_block_device_mount - Mount (load) the media of device dev * @dev: I2O device which should receive the mount request @@ -298,28 +322,31 @@ static inline void i2o_block_request_free(struct i2o_block_request *ireq) /** * i2o_block_sglist_alloc - Allocate the SG list and map it + * @c: I2O controller to which the request belongs * @ireq: I2O block request * - * Builds the SG list and map it into to be accessable by the controller. + * Builds the SG list and map it to be accessable by the controller. * - * Returns the number of elements in the SG list or 0 on failure. + * Returns 0 on failure or 1 on success. */ -static inline int i2o_block_sglist_alloc(struct i2o_block_request *ireq) +static inline int i2o_block_sglist_alloc(struct i2o_controller *c, + struct i2o_block_request *ireq, + u32 __iomem ** mptr) { - struct device *dev = &ireq->i2o_blk_dev->i2o_dev->iop->pdev->dev; int nents; + enum dma_data_direction direction; + ireq->dev = &c->pdev->dev; nents = blk_rq_map_sg(ireq->req->q, ireq->req, ireq->sg_table); if (rq_data_dir(ireq->req) == READ) - ireq->sg_dma_direction = PCI_DMA_FROMDEVICE; + direction = PCI_DMA_FROMDEVICE; else - ireq->sg_dma_direction = PCI_DMA_TODEVICE; + direction = PCI_DMA_TODEVICE; - ireq->sg_nents = dma_map_sg(dev, ireq->sg_table, nents, - ireq->sg_dma_direction); + ireq->sg_nents = nents; - return ireq->sg_nents; + return i2o_dma_map_sg(c, ireq->sg_table, nents, direction, mptr); }; /** @@ -330,10 +357,14 @@ static inline int i2o_block_sglist_alloc(struct i2o_block_request *ireq) */ static inline void i2o_block_sglist_free(struct i2o_block_request *ireq) { - struct device *dev = &ireq->i2o_blk_dev->i2o_dev->iop->pdev->dev; + enum dma_data_direction direction; - dma_unmap_sg(dev, ireq->sg_table, ireq->sg_nents, - ireq->sg_dma_direction); + if (rq_data_dir(ireq->req) == READ) + direction = PCI_DMA_FROMDEVICE; + else + direction = PCI_DMA_TODEVICE; + + dma_unmap_sg(ireq->dev, ireq->sg_table, ireq->sg_nents, direction); }; /** @@ -351,6 +382,11 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req) struct i2o_block_device *i2o_blk_dev = q->queuedata; struct i2o_block_request *ireq; + if (unlikely(!i2o_blk_dev)) { + osm_err("block device already removed\n"); + return BLKPREP_KILL; + } + /* request is already processed by us, so return */ if (req->flags & REQ_SPECIAL) { osm_debug("REQ_SPECIAL already set!\n"); @@ -399,6 +435,52 @@ static void i2o_block_delayed_request_fn(void *delayed_request) kfree(dreq); }; +/** + * i2o_block_end_request - Post-processing of completed commands + * @req: request which should be completed + * @uptodate: 1 for success, 0 for I/O error, < 0 for specific error + * @nr_bytes: number of bytes to complete + * + * Mark the request as complete. The lock must not be held when entering. + * + */ +static void i2o_block_end_request(struct request *req, int uptodate, + int nr_bytes) +{ + struct i2o_block_request *ireq = req->special; + struct i2o_block_device *dev = ireq->i2o_blk_dev; + request_queue_t *q = req->q; + unsigned long flags; + + if (end_that_request_chunk(req, uptodate, nr_bytes)) { + int leftover = (req->hard_nr_sectors << KERNEL_SECTOR_SHIFT); + + if (blk_pc_request(req)) + leftover = req->data_len; + + if (end_io_error(uptodate)) + end_that_request_chunk(req, 0, leftover); + } + + add_disk_randomness(req->rq_disk); + + spin_lock_irqsave(q->queue_lock, flags); + + end_that_request_last(req); + + if (likely(dev)) { + dev->open_queue_depth--; + list_del(&ireq->queue); + } + + blk_start_queue(q); + + spin_unlock_irqrestore(q->queue_lock, flags); + + i2o_block_sglist_free(ireq); + i2o_block_request_free(ireq); +}; + /** * i2o_block_reply - Block OSM reply handler. * @c: I2O controller from which the message arrives @@ -411,60 +493,8 @@ static void i2o_block_delayed_request_fn(void *delayed_request) static int i2o_block_reply(struct i2o_controller *c, u32 m, struct i2o_message *msg) { - struct i2o_block_request *ireq; struct request *req; - struct i2o_block_device *dev; - struct request_queue *q; - u8 st; - unsigned long flags; - - /* FAILed message */ - if (unlikely(le32_to_cpu(msg->u.head[0]) & (1 << 13))) { - struct i2o_message *pmsg; - u32 pm; - - /* - * FAILed message from controller - * We increment the error count and abort it - * - * In theory this will never happen. The I2O block class - * specification 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 :) - */ - pm = le32_to_cpu(msg->body[3]); - pmsg = i2o_msg_in_to_virt(c, pm); - - req = i2o_cntxt_list_get(c, le32_to_cpu(pmsg->u.s.tcntxt)); - if (unlikely(!req)) { - osm_err("NULL reply received!\n"); - return -1; - } - - ireq = req->special; - dev = ireq->i2o_blk_dev; - q = dev->gd->queue; - - req->errors++; - - spin_lock_irqsave(q->queue_lock, flags); - - while (end_that_request_chunk(req, !req->errors, - le32_to_cpu(pmsg->body[1]))) ; - end_that_request_last(req); - - dev->open_queue_depth--; - list_del(&ireq->queue); - blk_start_queue(q); - - spin_unlock_irqrestore(q->queue_lock, flags); - - /* Now flush the message by making it a NOP */ - i2o_msg_nop(c, pm); - - return -1; - } + int uptodate = 1; req = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt)); if (unlikely(!req)) { @@ -472,61 +502,13 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, return -1; } - ireq = req->special; - dev = ireq->i2o_blk_dev; - q = dev->gd->queue; - - if (unlikely(!dev->i2o_dev)) { - /* - * 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... - */ - req->errors++; - osm_warn("Data transfer to deleted device!\n"); - spin_lock_irqsave(q->queue_lock, flags); - while (end_that_request_chunk - (req, !req->errors, le32_to_cpu(msg->body[1]))) ; - end_that_request_last(req); - - dev->open_queue_depth--; - list_del(&ireq->queue); - blk_start_queue(q); - - spin_unlock_irqrestore(q->queue_lock, flags); - return -1; - } - /* * Lets see what is cooking. We stuffed the * request in the context. */ - st = le32_to_cpu(msg->body[0]) >> 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 = le32_to_cpu(msg->body[0]) & 0xffff; - + if ((le32_to_cpu(msg->body[0]) >> 24) != 0) { + u32 status = le32_to_cpu(msg->body[0]); /* * Device not ready means two things. One is that the * the thing went offline (but not a removal media) @@ -539,40 +521,23 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m, * Don't stick a supertrak100 into cache aggressive modes */ - osm_err("block-osm: /dev/%s error: %s", dev->gd->disk_name, - bsa_errors[le32_to_cpu(msg->body[0]) & 0xffff]); - if (le32_to_cpu(msg->body[0]) & 0x00ff0000) - printk(KERN_ERR " - DDM attempted %d retries", - (le32_to_cpu(msg->body[0]) >> 16) & 0x00ff); - printk(KERN_ERR ".\n"); + osm_err("TID %03x error status: 0x%02x, detailed status: " + "0x%04x\n", (le32_to_cpu(msg->u.head[1]) >> 12 & 0xfff), + status >> 24, status & 0xffff); + req->errors++; - } else - req->errors = 0; - if (!end_that_request_chunk - (req, !req->errors, le32_to_cpu(msg->body[1]))) { - add_disk_randomness(req->rq_disk); - spin_lock_irqsave(q->queue_lock, flags); + uptodate = 0; + } - end_that_request_last(req); - - dev->open_queue_depth--; - list_del(&ireq->queue); - blk_start_queue(q); - - spin_unlock_irqrestore(q->queue_lock, flags); - - i2o_block_sglist_free(ireq); - i2o_block_request_free(ireq); - } else - osm_err("still remaining chunks\n"); + i2o_block_end_request(req, uptodate, le32_to_cpu(msg->body[1])); return 1; }; static void i2o_block_event(struct i2o_event *evt) { - osm_info("block-osm: event received\n"); + osm_debug("event received\n"); kfree(evt); }; @@ -778,18 +743,25 @@ static int i2o_block_media_changed(struct gendisk *disk) static int i2o_block_transfer(struct request *req) { struct i2o_block_device *dev = req->rq_disk->private_data; - struct i2o_controller *c = dev->i2o_dev->iop; + struct i2o_controller *c; int tid = dev->i2o_dev->lct_data.tid; struct i2o_message __iomem *msg; - void __iomem *mptr; + u32 __iomem *mptr; struct i2o_block_request *ireq = req->special; - struct scatterlist *sg; - int sgnum; - int i; u32 m; u32 tcntxt; - u32 sg_flags; + u32 sgl_offset = SGL_OFFSET_8; + u32 ctl_flags = 0x00000000; int rc; + u32 cmd; + + if (unlikely(!dev->i2o_dev)) { + osm_err("transfer to removed drive\n"); + rc = -ENODEV; + goto exit; + } + + c = dev->i2o_dev->iop; m = i2o_msg_get(c, &msg); if (m == I2O_QUEUE_EMPTY) { @@ -803,82 +775,109 @@ static int i2o_block_transfer(struct request *req) goto nop_msg; } - if ((sgnum = i2o_block_sglist_alloc(ireq)) <= 0) { + writel(i2o_block_driver.context, &msg->u.s.icntxt); + writel(tcntxt, &msg->u.s.tcntxt); + + mptr = &msg->body[0]; + + if (rq_data_dir(req) == READ) { + cmd = I2O_CMD_BLOCK_READ << 24; + + switch (dev->rcache) { + case CACHE_PREFETCH: + ctl_flags = 0x201F0008; + break; + + case CACHE_SMARTFETCH: + if (req->nr_sectors > 16) + ctl_flags = 0x201F0008; + else + ctl_flags = 0x001F0000; + break; + + default: + break; + } + } else { + cmd = I2O_CMD_BLOCK_WRITE << 24; + + switch (dev->wcache) { + case CACHE_WRITETHROUGH: + ctl_flags = 0x001F0008; + break; + case CACHE_WRITEBACK: + ctl_flags = 0x001F0010; + break; + case CACHE_SMARTBACK: + if (req->nr_sectors > 16) + ctl_flags = 0x001F0004; + else + ctl_flags = 0x001F0010; + break; + case CACHE_SMARTTHROUGH: + if (req->nr_sectors > 16) + ctl_flags = 0x001F0004; + else + ctl_flags = 0x001F0010; + default: + break; + } + } + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + u8 cmd[10]; + u32 scsi_flags; + u16 hwsec = queue_hardsect_size(req->q) >> KERNEL_SECTOR_SHIFT; + + memset(cmd, 0, 10); + + sgl_offset = SGL_OFFSET_12; + + writel(I2O_CMD_PRIVATE << 24 | HOST_TID << 12 | tid, + &msg->u.head[1]); + + writel(I2O_VENDOR_DPT << 16 | I2O_CMD_SCSI_EXEC, mptr++); + writel(tid, mptr++); + + /* + * ENABLE_DISCONNECT + * SIMPLE_TAG + * RETURN_SENSE_DATA_IN_REPLY_MESSAGE_FRAME + */ + if (rq_data_dir(req) == READ) { + cmd[0] = 0x28; + scsi_flags = 0x60a0000a; + } else { + cmd[0] = 0x2A; + scsi_flags = 0xa0a0000a; + } + + writel(scsi_flags, mptr++); + + *((u32 *) & cmd[2]) = cpu_to_be32(req->sector * hwsec); + *((u16 *) & cmd[7]) = cpu_to_be16(req->nr_sectors * hwsec); + + memcpy_toio(mptr, cmd, 10); + mptr += 4; + writel(req->nr_sectors << KERNEL_SECTOR_SHIFT, mptr++); + } else +#endif + { + writel(cmd | HOST_TID << 12 | tid, &msg->u.head[1]); + writel(ctl_flags, mptr++); + writel(req->nr_sectors << KERNEL_SECTOR_SHIFT, mptr++); + writel((u32) (req->sector << KERNEL_SECTOR_SHIFT), mptr++); + writel(req->sector >> (32 - KERNEL_SECTOR_SHIFT), mptr++); + } + + if (!i2o_block_sglist_alloc(c, ireq, &mptr)) { rc = -ENOMEM; goto context_remove; } - /* Build the message based on the request. */ - writel(i2o_block_driver.context, &msg->u.s.icntxt); - writel(tcntxt, &msg->u.s.tcntxt); - writel(req->nr_sectors << 9, &msg->body[1]); - - writel((((u64) req->sector) << 9) & 0xffffffff, &msg->body[2]); - writel(req->sector >> 23, &msg->body[3]); - - mptr = &msg->body[4]; - - sg = ireq->sg_table; - - if (rq_data_dir(req) == READ) { - writel(I2O_CMD_BLOCK_READ << 24 | HOST_TID << 12 | tid, - &msg->u.head[1]); - sg_flags = 0x10000000; - switch (dev->rcache) { - case CACHE_NULL: - writel(0, &msg->body[0]); - break; - case CACHE_PREFETCH: - writel(0x201F0008, &msg->body[0]); - break; - case CACHE_SMARTFETCH: - if (req->nr_sectors > 16) - writel(0x201F0008, &msg->body[0]); - else - writel(0x001F0000, &msg->body[0]); - break; - } - } else { - writel(I2O_CMD_BLOCK_WRITE << 24 | HOST_TID << 12 | tid, - &msg->u.head[1]); - sg_flags = 0x14000000; - switch (dev->wcache) { - case CACHE_NULL: - writel(0, &msg->body[0]); - break; - case CACHE_WRITETHROUGH: - writel(0x001F0008, &msg->body[0]); - break; - case CACHE_WRITEBACK: - writel(0x001F0010, &msg->body[0]); - break; - case CACHE_SMARTBACK: - if (req->nr_sectors > 16) - writel(0x001F0004, &msg->body[0]); - else - writel(0x001F0010, &msg->body[0]); - break; - case CACHE_SMARTTHROUGH: - if (req->nr_sectors > 16) - writel(0x001F0004, &msg->body[0]); - else - writel(0x001F0010, &msg->body[0]); - } - } - - for (i = sgnum; i > 0; i--) { - if (i == 1) - sg_flags |= 0x80000000; - writel(sg_flags | sg_dma_len(sg), mptr); - writel(sg_dma_address(sg), mptr + 4); - mptr += 8; - sg++; - } - - writel(I2O_MESSAGE_SIZE - (((unsigned long)mptr - - (unsigned long)&msg->u.head[0]) >> 2) | SGL_OFFSET_8, - &msg->u.head[0]); + writel(I2O_MESSAGE_SIZE(mptr - &msg->u.head[0]) | + sgl_offset, &msg->u.head[0]); list_add_tail(&ireq->queue, &dev->open_queue); dev->open_queue_depth++; @@ -921,11 +920,13 @@ static void i2o_block_request_fn(struct request_queue *q) queue_depth = ireq->i2o_blk_dev->open_queue_depth; - if (queue_depth < I2O_BLOCK_MAX_OPEN_REQUESTS) + if (queue_depth < I2O_BLOCK_MAX_OPEN_REQUESTS) { if (!i2o_block_transfer(req)) { blkdev_dequeue_request(req); continue; - } + } else + osm_info("transfer error\n"); + } if (queue_depth) break; @@ -939,7 +940,6 @@ static void i2o_block_request_fn(struct request_queue *q) INIT_WORK(&dreq->work, i2o_block_delayed_request_fn, dreq); - osm_info("transfer error\n"); if (!queue_delayed_work(i2o_block_driver.event_queue, &dreq->work, I2O_BLOCK_RETRY_TIME)) @@ -1008,6 +1008,7 @@ static struct i2o_block_device *i2o_block_device_alloc(void) } blk_queue_prep_rq(queue, i2o_block_prep_req_fn); + blk_queue_issue_flush_fn(queue, i2o_block_issue_flush); gd->major = I2O_MAJOR; gd->queue = queue; @@ -1040,17 +1041,27 @@ static struct i2o_block_device *i2o_block_device_alloc(void) static int i2o_block_probe(struct device *dev) { struct i2o_device *i2o_dev = to_i2o_device(dev); - struct i2o_block_device *i2o_blk_dev; struct i2o_controller *c = i2o_dev->iop; + struct i2o_block_device *i2o_blk_dev; struct gendisk *gd; struct request_queue *queue; static int unit = 0; int rc; u64 size; u32 blocksize; - u16 power; u32 flags, status; - int segments; + u16 body_size = 4; + unsigned short max_sectors; + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) + body_size = 8; +#endif + + if (c->limit_sectors) + max_sectors = I2O_MAX_SECTORS_LIMITED; + else + max_sectors = I2O_MAX_SECTORS; /* skip devices which are used by IOP */ if (i2o_dev->lct_data.user_tid != 0xfff) { @@ -1058,8 +1069,6 @@ static int i2o_block_probe(struct device *dev) return -ENODEV; } - osm_info("New device detected (TID: %03x)\n", i2o_dev->lct_data.tid); - if (i2o_device_claim(i2o_dev)) { osm_warn("Unable to claim device. Installation aborted\n"); rc = -EFAULT; @@ -1087,50 +1096,44 @@ static int i2o_block_probe(struct device *dev) queue = gd->queue; queue->queuedata = i2o_blk_dev; - blk_queue_max_phys_segments(queue, I2O_MAX_SEGMENTS); - blk_queue_max_sectors(queue, I2O_MAX_SECTORS); + blk_queue_max_phys_segments(queue, I2O_MAX_PHYS_SEGMENTS); + blk_queue_max_sectors(queue, max_sectors); + blk_queue_max_hw_segments(queue, i2o_sg_tablesize(c, body_size)); - if (c->short_req) - segments = 8; - else { - i2o_status_block *sb; - - sb = c->status_block.virt; - - segments = (sb->inbound_frame_size - - sizeof(struct i2o_message) / 4 - 4) / 2; - } - - blk_queue_max_hw_segments(queue, segments); - - osm_debug("max sectors = %d\n", I2O_MAX_SECTORS); - osm_debug("phys segments = %d\n", I2O_MAX_SEGMENTS); - osm_debug("hw segments = %d\n", segments); + osm_debug("max sectors = %d\n", queue->max_phys_segments); + osm_debug("phys segments = %d\n", queue->max_sectors); + osm_debug("max hw segments = %d\n", queue->max_hw_segments); /* * Ask for the current media data. If that isn't supported * then we ask for the device capacity data */ - if (i2o_parm_field_get(i2o_dev, 0x0004, 1, &blocksize, 4) != 0 - || i2o_parm_field_get(i2o_dev, 0x0004, 0, &size, 8) != 0) { - i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4); - i2o_parm_field_get(i2o_dev, 0x0000, 4, &size, 8); - } - osm_debug("blocksize = %d\n", blocksize); + if (i2o_parm_field_get(i2o_dev, 0x0004, 1, &blocksize, 4) || + i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4)) { + blk_queue_hardsect_size(queue, blocksize); + } else + osm_warn("unable to get blocksize of %s\n", gd->disk_name); - if (i2o_parm_field_get(i2o_dev, 0x0000, 2, &power, 2)) - power = 0; + if (i2o_parm_field_get(i2o_dev, 0x0004, 0, &size, 8) || + i2o_parm_field_get(i2o_dev, 0x0000, 4, &size, 8)) { + set_capacity(gd, size >> KERNEL_SECTOR_SHIFT); + } else + osm_warn("could not get size of %s\n", gd->disk_name); + + if (!i2o_parm_field_get(i2o_dev, 0x0000, 2, &i2o_blk_dev->power, 2)) + i2o_blk_dev->power = 0; i2o_parm_field_get(i2o_dev, 0x0000, 5, &flags, 4); i2o_parm_field_get(i2o_dev, 0x0000, 6, &status, 4); - set_capacity(gd, size >> 9); - i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0xffffffff); add_disk(gd); unit++; + osm_info("device added (TID: %03x): %s\n", i2o_dev->lct_data.tid, + i2o_blk_dev->gd->disk_name); + return 0; claim_release: @@ -1178,7 +1181,7 @@ static int __init i2o_block_init(void) goto exit; } - i2o_blk_req_pool.pool = mempool_create(I2O_REQ_MEMPOOL_SIZE, + i2o_blk_req_pool.pool = mempool_create(I2O_BLOCK_REQ_MEMPOOL_SIZE, mempool_alloc_slab, mempool_free_slab, i2o_blk_req_pool.slab); diff --git a/drivers/message/i2o/i2o_block.h b/drivers/message/i2o/i2o_block.h index ddd9a15679c0..4fdaa5bda412 100644 --- a/drivers/message/i2o/i2o_block.h +++ b/drivers/message/i2o/i2o_block.h @@ -56,42 +56,46 @@ #define I2O_BLOCK_RETRY_TIME HZ/4 #define I2O_BLOCK_MAX_OPEN_REQUESTS 50 +/* request queue sizes */ +#define I2O_BLOCK_REQ_MEMPOOL_SIZE 32 + +#define KERNEL_SECTOR_SHIFT 9 +#define KERNEL_SECTOR_SIZE (1 << KERNEL_SECTOR_SHIFT) + /* I2O Block OSM mempool struct */ struct i2o_block_mempool { - kmem_cache_t *slab; - mempool_t *pool; + kmem_cache_t *slab; + mempool_t *pool; }; /* I2O Block device descriptor */ struct i2o_block_device { struct i2o_device *i2o_dev; /* pointer to I2O device */ struct gendisk *gd; - spinlock_t lock; /* queue lock */ + spinlock_t lock; /* queue lock */ struct list_head open_queue; /* list of transfered, but unfinished requests */ unsigned int open_queue_depth; /* number of requests in the queue */ - int rcache; /* read cache flags */ - int wcache; /* write cache flags */ + int rcache; /* read cache flags */ + int wcache; /* write cache flags */ int flags; - int power; /* power state */ - int media_change_flag; /* media changed flag */ + u16 power; /* power state */ + int media_change_flag; /* media changed flag */ }; /* I2O Block device request */ -struct i2o_block_request -{ +struct i2o_block_request { struct list_head queue; - struct request *req; /* corresponding request */ + struct request *req; /* corresponding request */ struct i2o_block_device *i2o_blk_dev; /* I2O block device */ - int sg_dma_direction; /* direction of DMA buffer read/write */ - int sg_nents; /* number of SG elements */ - struct scatterlist sg_table[I2O_MAX_SEGMENTS]; /* SG table */ + struct device *dev; /* device used for DMA */ + int sg_nents; /* number of SG elements */ + struct scatterlist sg_table[I2O_MAX_PHYS_SEGMENTS]; /* SG table */ }; /* I2O Block device delayed request */ -struct i2o_block_delayed_request -{ +struct i2o_block_delayed_request { struct work_struct work; struct request_queue *queue; }; diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c index 1fb5cdf67f8f..3c3a7abebb1b 100644 --- a/drivers/message/i2o/i2o_config.c +++ b/drivers/message/i2o/i2o_config.c @@ -30,29 +30,15 @@ * 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 OSM_NAME "config-osm" -#define OSM_VERSION "$Rev$" -#define OSM_DESCRIPTION "I2O Configuration OSM" +#include "core.h" -extern int i2o_parm_issue(struct i2o_device *, int, void *, int, void *, int); +#define SG_TABLESIZE 30 static int i2o_cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg); @@ -80,15 +66,6 @@ struct i2o_cfg_info { static struct i2o_cfg_info *open_files = NULL; static ulong i2o_cfg_info_id = 0; -/* - * Each of these describes an i2o message handler. They are - * multiplexed by the i2o_core code - */ - -static struct i2o_driver i2o_config_driver = { - .name = OSM_NAME -}; - static int i2o_cfg_getiops(unsigned long arg) { struct i2o_controller *c; @@ -391,9 +368,9 @@ static int i2o_cfg_swul(unsigned long arg) i2o_dma_free(&c->pdev->dev, &buffer); -return_ret: + return_ret: return ret; -return_fault: + return_fault: ret = -EFAULT; goto return_ret; }; @@ -540,8 +517,10 @@ static int i2o_cfg_evt_get(unsigned long arg, struct file *fp) return 0; } +#ifdef CONFIG_I2O_EXT_ADAPTEC #ifdef CONFIG_COMPAT -static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long arg) +static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, + unsigned long arg) { struct i2o_cmd_passthru32 __user *cmd; struct i2o_controller *c; @@ -555,6 +534,7 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar u32 sg_offset = 0; u32 sg_count = 0; u32 i = 0; + u32 sg_index = 0; i2o_status_block *sb; struct i2o_message *msg; u32 m; @@ -634,8 +614,8 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar if (sg_count > SG_TABLESIZE) { printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n", c->name, sg_count); - kfree(reply); - return -EINVAL; + rcode = -EINVAL; + goto cleanup; } for (i = 0; i < sg_count; i++) { @@ -651,7 +631,7 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar goto cleanup; } sg_size = sg[i].flag_count & 0xffffff; - p = &(sg_list[i]); + p = &(sg_list[sg_index++]); /* Allocate memory for the transfer */ if (i2o_dma_alloc (&c->pdev->dev, p, sg_size, @@ -660,20 +640,21 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n", c->name, sg_size, i, sg_count); rcode = -ENOMEM; - goto cleanup; + goto sg_list_cleanup; } /* Copy in the user's SG buffer if necessary */ if (sg[i]. flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) { // TODO 64bit fix if (copy_from_user - (p->virt, (void __user *)(unsigned long)sg[i].addr_bus, - sg_size)) { + (p->virt, + (void __user *)(unsigned long)sg[i]. + addr_bus, sg_size)) { printk(KERN_DEBUG "%s: Could not copy SG buf %d FROM user\n", c->name, i); rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } } //TODO 64bit fix @@ -683,10 +664,10 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar rcode = i2o_msg_post_wait(c, m, 60); if (rcode) - goto cleanup; + goto sg_list_cleanup; if (sg_offset) { - u32 msg[128]; + u32 msg[I2O_OUTBOUND_MSG_FRAME_SIZE]; /* Copy back the Scatter Gather buffers back to user space */ u32 j; // TODO 64bit fix @@ -694,18 +675,18 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar int sg_size; // re-acquire the original message to handle correctly the sg copy operation - memset(&msg, 0, MSG_FRAME_SIZE * 4); + memset(&msg, 0, I2O_OUTBOUND_MSG_FRAME_SIZE * 4); // get user msg size in u32s if (get_user(size, &user_msg[0])) { rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } size = size >> 16; size *= 4; /* Copy in the user's I2O command */ if (copy_from_user(msg, user_msg, size)) { rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } sg_count = (size - sg_offset * 4) / sizeof(struct sg_simple_element); @@ -727,7 +708,7 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar c->name, sg_list[j].virt, sg[j].addr_bus); rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } } } @@ -741,6 +722,7 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar "%s: Could not copy message context FROM user\n", c->name); rcode = -EFAULT; + goto sg_list_cleanup; } if (copy_to_user(user_reply, reply, reply_size)) { printk(KERN_WARNING @@ -749,16 +731,21 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd, unsigned long ar } } + sg_list_cleanup: + for (i = 0; i < sg_index; i++) + i2o_dma_free(&c->pdev->dev, &sg_list[i]); + cleanup: kfree(reply); return rcode; } -static long i2o_cfg_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) +static long i2o_cfg_compat_ioctl(struct file *file, unsigned cmd, + unsigned long arg) { int ret; - lock_kernel(); - switch (cmd) { + lock_kernel(); + switch (cmd) { case I2OGETIOPS: ret = i2o_cfg_ioctl(NULL, file, cmd, arg); break; @@ -862,8 +849,8 @@ static int i2o_cfg_passthru(unsigned long arg) if (sg_count > SG_TABLESIZE) { printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n", c->name, sg_count); - kfree(reply); - return -EINVAL; + rcode = -EINVAL; + goto cleanup; } for (i = 0; i < sg_count; i++) { @@ -875,7 +862,7 @@ static int i2o_cfg_passthru(unsigned long arg) "%s:Bad SG element %d - not simple (%x)\n", c->name, i, sg[i].flag_count); rcode = -EINVAL; - goto cleanup; + goto sg_list_cleanup; } sg_size = sg[i].flag_count & 0xffffff; /* Allocate memory for the transfer */ @@ -885,7 +872,7 @@ static int i2o_cfg_passthru(unsigned long arg) "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n", c->name, sg_size, i, sg_count); rcode = -ENOMEM; - goto cleanup; + goto sg_list_cleanup; } sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame. /* Copy in the user's SG buffer if necessary */ @@ -899,7 +886,7 @@ static int i2o_cfg_passthru(unsigned long arg) "%s: Could not copy SG buf %d FROM user\n", c->name, i); rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } } //TODO 64bit fix @@ -909,7 +896,7 @@ static int i2o_cfg_passthru(unsigned long arg) rcode = i2o_msg_post_wait(c, m, 60); if (rcode) - goto cleanup; + goto sg_list_cleanup; if (sg_offset) { u32 msg[128]; @@ -920,18 +907,18 @@ static int i2o_cfg_passthru(unsigned long arg) int sg_size; // re-acquire the original message to handle correctly the sg copy operation - memset(&msg, 0, MSG_FRAME_SIZE * 4); + memset(&msg, 0, I2O_OUTBOUND_MSG_FRAME_SIZE * 4); // get user msg size in u32s if (get_user(size, &user_msg[0])) { rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } size = size >> 16; size *= 4; /* Copy in the user's I2O command */ if (copy_from_user(msg, user_msg, size)) { rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } sg_count = (size - sg_offset * 4) / sizeof(struct sg_simple_element); @@ -953,7 +940,7 @@ static int i2o_cfg_passthru(unsigned long arg) c->name, sg_list[j], sg[j].addr_bus); rcode = -EFAULT; - goto cleanup; + goto sg_list_cleanup; } } } @@ -975,10 +962,15 @@ static int i2o_cfg_passthru(unsigned long arg) } } + sg_list_cleanup: + for (i = 0; i < sg_index; i++) + kfree(sg_list[i]); + cleanup: kfree(reply); return rcode; } +#endif /* * IOCTL Handler @@ -1033,9 +1025,11 @@ static int i2o_cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, ret = i2o_cfg_evt_get(arg, fp); break; +#ifdef CONFIG_I2O_EXT_ADAPTEC case I2OPASSTHRU: ret = i2o_cfg_passthru(arg); break; +#endif default: osm_debug("unknown ioctl called!\n"); @@ -1137,37 +1131,21 @@ static struct miscdevice i2o_miscdev = { &config_fops }; -static int __init i2o_config_init(void) +static int __init i2o_config_old_init(void) { - printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n"); - spin_lock_init(&i2o_config_lock); if (misc_register(&i2o_miscdev) < 0) { osm_err("can't register device.\n"); return -EBUSY; } - /* - * Install our handler - */ - if (i2o_driver_register(&i2o_config_driver)) { - osm_err("handler register failed.\n"); - misc_deregister(&i2o_miscdev); - return -EBUSY; - } + return 0; } -static void i2o_config_exit(void) +static void i2o_config_old_exit(void) { misc_deregister(&i2o_miscdev); - i2o_driver_unregister(&i2o_config_driver); } MODULE_AUTHOR("Red Hat Software"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(OSM_DESCRIPTION); -MODULE_VERSION(OSM_VERSION); - -module_init(i2o_config_init); -module_exit(i2o_config_exit); diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c index b176d0eeff7f..d559a1758363 100644 --- a/drivers/message/i2o/i2o_proc.c +++ b/drivers/message/i2o/i2o_proc.c @@ -28,7 +28,7 @@ */ #define OSM_NAME "proc-osm" -#define OSM_VERSION "$Rev$" +#define OSM_VERSION "1.145" #define OSM_DESCRIPTION "I2O ProcFS OSM" #define I2O_MAX_MODULES 4 @@ -228,7 +228,7 @@ static const char *i2o_get_class_name(int class) case I2O_CLASS_FLOPPY_DEVICE: idx = 12; break; - case I2O_CLASS_BUS_ADAPTER_PORT: + case I2O_CLASS_BUS_ADAPTER: idx = 13; break; case I2O_CLASS_PEER_TRANSPORT_AGENT: @@ -490,7 +490,7 @@ static int i2o_seq_show_lct(struct seq_file *seq, void *v) seq_printf(seq, ", Unknown Device Type"); break; - case I2O_CLASS_BUS_ADAPTER_PORT: + case I2O_CLASS_BUS_ADAPTER: if (lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) seq_printf(seq, ", %s", bus_ports[lct->lct_entry[i]. diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c index 43f5875e0be5..9f1744c3933b 100644 --- a/drivers/message/i2o/i2o_scsi.c +++ b/drivers/message/i2o/i2o_scsi.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -64,19 +65,23 @@ #include #include #include +#include +#include +#include #define OSM_NAME "scsi-osm" -#define OSM_VERSION "$Rev$" +#define OSM_VERSION "1.282" #define OSM_DESCRIPTION "I2O SCSI Peripheral OSM" static struct i2o_driver i2o_scsi_driver; -static int i2o_scsi_max_id = 16; -static int i2o_scsi_max_lun = 8; +static unsigned int i2o_scsi_max_id = 16; +static unsigned int i2o_scsi_max_lun = 255; struct i2o_scsi_host { struct Scsi_Host *scsi_host; /* pointer to the SCSI host */ struct i2o_controller *iop; /* pointer to the I2O controller */ + unsigned int lun; /* lun's used for block devices */ struct i2o_device *channel[0]; /* channel->i2o_dev mapping table */ }; @@ -99,11 +104,17 @@ static struct i2o_scsi_host *i2o_scsi_host_alloc(struct i2o_controller *c) u8 type; int i; size_t size; - i2o_status_block *sb; + u16 body_size = 6; + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) + body_size = 8; +#endif list_for_each_entry(i2o_dev, &c->devices, list) - if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT) { - if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) || (type == 1)) /* SCSI bus */ + if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER) { + if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) + && (type == 0x01)) /* SCSI bus */ max_channel++; } @@ -125,20 +136,18 @@ static struct i2o_scsi_host *i2o_scsi_host_alloc(struct i2o_controller *c) scsi_host->max_id = i2o_scsi_max_id; scsi_host->max_lun = i2o_scsi_max_lun; scsi_host->this_id = c->unit; - - sb = c->status_block.virt; - - scsi_host->sg_tablesize = (sb->inbound_frame_size - - sizeof(struct i2o_message) / 4 - 6) / 2; + scsi_host->sg_tablesize = i2o_sg_tablesize(c, body_size); i2o_shost = (struct i2o_scsi_host *)scsi_host->hostdata; i2o_shost->scsi_host = scsi_host; i2o_shost->iop = c; + i2o_shost->lun = 1; i = 0; list_for_each_entry(i2o_dev, &c->devices, list) - if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT) { - if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) || (type == 1)) /* only SCSI bus */ + if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER) { + if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) + && (type == 0x01)) /* only SCSI bus */ i2o_shost->channel[i++] = i2o_dev; if (i >= max_channel) @@ -178,10 +187,13 @@ static int i2o_scsi_remove(struct device *dev) struct i2o_scsi_host *i2o_shost; struct scsi_device *scsi_dev; + osm_info("device removed (TID: %03x)\n", i2o_dev->lct_data.tid); + i2o_shost = i2o_scsi_get_host(c); shost_for_each_device(scsi_dev, i2o_shost->scsi_host) if (scsi_dev->hostdata == i2o_dev) { + sysfs_remove_link(&i2o_dev->device.kobj, "scsi"); scsi_remove_device(scsi_dev); scsi_device_put(scsi_dev); break; @@ -207,8 +219,8 @@ static int i2o_scsi_probe(struct device *dev) struct Scsi_Host *scsi_host; struct i2o_device *parent; struct scsi_device *scsi_dev; - u32 id; - u64 lun; + u32 id = -1; + u64 lun = -1; int channel = -1; int i; @@ -218,51 +230,83 @@ static int i2o_scsi_probe(struct device *dev) scsi_host = i2o_shost->scsi_host; - if (i2o_parm_field_get(i2o_dev, 0, 3, &id, 4) < 0) - return -EFAULT; + switch (i2o_dev->lct_data.class_id) { + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + case I2O_CLASS_EXECUTIVE: +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + u8 type; + struct i2o_device *d = i2o_shost->channel[0]; - if (id >= scsi_host->max_id) { - osm_warn("SCSI device id (%d) >= max_id of I2O host (%d)", id, - scsi_host->max_id); + if (i2o_parm_field_get(d, 0x0000, 0, &type, 1) + && (type == 0x01)) /* SCSI bus */ + if (i2o_parm_field_get(d, 0x0200, 4, &id, 4)) { + channel = 0; + if (i2o_dev->lct_data.class_id == + I2O_CLASS_RANDOM_BLOCK_STORAGE) + lun = i2o_shost->lun++; + else + lun = 0; + } + } +#endif + break; + + case I2O_CLASS_SCSI_PERIPHERAL: + if (i2o_parm_field_get(i2o_dev, 0x0000, 3, &id, 4) < 0) + return -EFAULT; + + if (i2o_parm_field_get(i2o_dev, 0x0000, 4, &lun, 8) < 0) + return -EFAULT; + + parent = i2o_iop_find_device(c, i2o_dev->lct_data.parent_tid); + if (!parent) { + osm_warn("can not find parent of device %03x\n", + i2o_dev->lct_data.tid); + return -EFAULT; + } + + for (i = 0; i <= i2o_shost->scsi_host->max_channel; i++) + if (i2o_shost->channel[i] == parent) + channel = i; + break; + + default: return -EFAULT; } - if (i2o_parm_field_get(i2o_dev, 0, 4, &lun, 8) < 0) - return -EFAULT; - if (lun >= scsi_host->max_lun) { - osm_warn("SCSI device id (%d) >= max_lun of I2O host (%d)", - (unsigned int)lun, scsi_host->max_lun); - return -EFAULT; - } - - parent = i2o_iop_find_device(c, i2o_dev->lct_data.parent_tid); - if (!parent) { - osm_warn("can not find parent of device %03x\n", - i2o_dev->lct_data.tid); - return -EFAULT; - } - - for (i = 0; i <= i2o_shost->scsi_host->max_channel; i++) - if (i2o_shost->channel[i] == parent) - channel = i; - if (channel == -1) { osm_warn("can not find channel of device %03x\n", i2o_dev->lct_data.tid); return -EFAULT; } - scsi_dev = - __scsi_add_device(i2o_shost->scsi_host, channel, id, lun, i2o_dev); - - if (!scsi_dev) { - osm_warn("can not add SCSI device %03x\n", - i2o_dev->lct_data.tid); + if (id >= scsi_host->max_id) { + osm_warn("SCSI device id (%d) >= max_id of I2O host (%d)", id, + scsi_host->max_id); return -EFAULT; } - osm_debug("added new SCSI device %03x (cannel: %d, id: %d, lun: %d)\n", - i2o_dev->lct_data.tid, channel, id, (unsigned int)lun); + if (lun >= scsi_host->max_lun) { + osm_warn("SCSI device id (%d) >= max_lun of I2O host (%d)", + (unsigned int)lun, scsi_host->max_lun); + return -EFAULT; + } + + scsi_dev = + __scsi_add_device(i2o_shost->scsi_host, channel, id, lun, i2o_dev); + + if (IS_ERR(scsi_dev)) { + osm_warn("can not add SCSI device %03x\n", + i2o_dev->lct_data.tid); + return PTR_ERR(scsi_dev); + } + + sysfs_create_link(&i2o_dev->device.kobj, &scsi_dev->sdev_gendev.kobj, + "scsi"); + + osm_info("device added (TID: %03x) channel: %d, id: %d, lun: %d\n", + i2o_dev->lct_data.tid, channel, id, (unsigned int)lun); return 0; }; @@ -293,164 +337,91 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m, struct i2o_message *msg) { struct scsi_cmnd *cmd; + u32 error; struct device *dev; - u8 as, ds, st; cmd = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt)); - - if (msg->u.head[0] & (1 << 13)) { - struct i2o_message __iomem *pmsg; /* preserved message */ - u32 pm; - int err = DID_ERROR; - - pm = le32_to_cpu(msg->body[3]); - - pmsg = i2o_msg_in_to_virt(c, pm); - - osm_err("IOP fail.\n"); - osm_err("From %d To %d Cmd %d.\n", - (msg->u.head[1] >> 12) & 0xFFF, - msg->u.head[1] & 0xFFF, msg->u.head[1] >> 24); - osm_err("Failure Code %d.\n", msg->body[0] >> 24); - if (msg->body[0] & (1 << 16)) - osm_err("Format error.\n"); - if (msg->body[0] & (1 << 17)) - osm_err("Path error.\n"); - if (msg->body[0] & (1 << 18)) - osm_err("Path State.\n"); - if (msg->body[0] & (1 << 18)) - { - osm_err("Congestion.\n"); - err = DID_BUS_BUSY; - } - - osm_debug("Failing message is %p.\n", pmsg); - - cmd = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt)); - if (!cmd) - return 1; - - cmd->result = err << 16; - cmd->scsi_done(cmd); - - /* Now flush the message by making it a NOP */ - i2o_msg_nop(c, pm); - - return 1; + if (unlikely(!cmd)) { + osm_err("NULL reply received!\n"); + return -1; } /* * Low byte is device status, next is adapter status, * (then one byte reserved), then request status. */ - ds = (u8) le32_to_cpu(msg->body[0]); - as = (u8) (le32_to_cpu(msg->body[0]) >> 8); - st = (u8) (le32_to_cpu(msg->body[0]) >> 24); - - /* - * Is this a control request coming back - eg an abort ? - */ - - if (!cmd) { - if (st) - osm_warn("SCSI abort: %08X", le32_to_cpu(msg->body[0])); - osm_info("SCSI abort completed.\n"); - return -EFAULT; - } + error = le32_to_cpu(msg->body[0]); osm_debug("Completed %ld\n", cmd->serial_number); - if (st) { - u32 count, error; - /* An error has occurred */ + cmd->result = error & 0xff; + /* + * if DeviceStatus is not SCSI_SUCCESS copy over the sense data and let + * the SCSI layer handle the error + */ + if (cmd->result) + memcpy(cmd->sense_buffer, &msg->body[3], + min(sizeof(cmd->sense_buffer), (size_t) 40)); - switch (st) { - case 0x06: - count = le32_to_cpu(msg->body[1]); - if (count < cmd->underflow) { - int i; - - osm_err("SCSI underflow 0x%08X 0x%08X\n", count, - cmd->underflow); - osm_debug("Cmd: "); - for (i = 0; i < 15; i++) - pr_debug("%02X ", cmd->cmnd[i]); - pr_debug(".\n"); - cmd->result = (DID_ERROR << 16); - } - break; - - default: - error = le32_to_cpu(msg->body[0]); - - osm_err("SCSI error %08x\n", error); - - if ((error & 0xff) == 0x02 /*CHECK_CONDITION */ ) { - int i; - u32 len = sizeof(cmd->sense_buffer); - len = (len > 40) ? 40 : len; - // Copy over the sense data - memcpy(cmd->sense_buffer, (void *)&msg->body[3], - len); - for (i = 0; i <= len; i++) - osm_info("%02x\n", - cmd->sense_buffer[i]); - if (cmd->sense_buffer[0] == 0x70 - && cmd->sense_buffer[2] == DATA_PROTECT) { - /* This is to handle an array failed */ - cmd->result = (DID_TIME_OUT << 16); - printk(KERN_WARNING "%s: SCSI Data " - "Protect-Device (%d,%d,%d) " - "hba_status=0x%x, dev_status=" - "0x%x, cmd=0x%x\n", c->name, - (u32) cmd->device->channel, - (u32) cmd->device->id, - (u32) cmd->device->lun, - (error >> 8) & 0xff, - error & 0xff, cmd->cmnd[0]); - } else - cmd->result = (DID_ERROR << 16); - - break; - } - - switch (as) { - case 0x0E: - /* SCSI Reset */ - cmd->result = DID_RESET << 16; - break; - - case 0x0F: - cmd->result = DID_PARITY << 16; - break; - - default: - cmd->result = DID_ERROR << 16; - break; - } - - break; - } - - cmd->scsi_done(cmd); - return 1; - } - - cmd->result = DID_OK << 16 | ds; - - cmd->scsi_done(cmd); + /* only output error code if AdapterStatus is not HBA_SUCCESS */ + if ((error >> 8) & 0xff) + osm_err("SCSI error %08x\n", error); dev = &c->pdev->dev; if (cmd->use_sg) - dma_unmap_sg(dev, (struct scatterlist *)cmd->buffer, - cmd->use_sg, cmd->sc_data_direction); - else if (cmd->request_bufflen) - dma_unmap_single(dev, (dma_addr_t) ((long)cmd->SCp.ptr), - cmd->request_bufflen, cmd->sc_data_direction); + dma_unmap_sg(dev, cmd->request_buffer, cmd->use_sg, + cmd->sc_data_direction); + else if (cmd->SCp.dma_handle) + dma_unmap_single(dev, cmd->SCp.dma_handle, cmd->request_bufflen, + cmd->sc_data_direction); + + cmd->scsi_done(cmd); return 1; }; +/** + * i2o_scsi_notify_device_add - Retrieve notifications of added devices + * @i2o_dev: the I2O device which was added + * + * If a I2O device is added we catch the notification, because I2O classes + * other then SCSI peripheral will not be received through + * i2o_scsi_probe(). + */ +static void i2o_scsi_notify_device_add(struct i2o_device *i2o_dev) +{ + switch (i2o_dev->lct_data.class_id) { + case I2O_CLASS_EXECUTIVE: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_scsi_probe(&i2o_dev->device); + break; + + default: + break; + } +}; + +/** + * i2o_scsi_notify_device_remove - Retrieve notifications of removed + * devices + * @i2o_dev: the I2O device which was removed + * + * If a I2O device is removed, we catch the notification to remove the + * corresponding SCSI device. + */ +static void i2o_scsi_notify_device_remove(struct i2o_device *i2o_dev) +{ + switch (i2o_dev->lct_data.class_id) { + case I2O_CLASS_EXECUTIVE: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_scsi_remove(&i2o_dev->device); + break; + + default: + break; + } +}; + /** * i2o_scsi_notify_controller_add - Retrieve notifications of added * controllers @@ -501,7 +472,7 @@ static void i2o_scsi_notify_controller_remove(struct i2o_controller *c) scsi_remove_host(i2o_shost->scsi_host); scsi_host_put(i2o_shost->scsi_host); - pr_info("I2O SCSI host removed\n"); + osm_debug("I2O SCSI host removed\n"); }; /* SCSI OSM driver struct */ @@ -509,6 +480,8 @@ static struct i2o_driver i2o_scsi_driver = { .name = OSM_NAME, .reply = i2o_scsi_reply, .classes = i2o_scsi_class_id, + .notify_device_add = i2o_scsi_notify_device_add, + .notify_device_remove = i2o_scsi_notify_device_remove, .notify_controller_add = i2o_scsi_notify_controller_add, .notify_controller_remove = i2o_scsi_notify_controller_remove, .driver = { @@ -535,26 +508,26 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, void (*done) (struct scsi_cmnd *)) { struct i2o_controller *c; - struct Scsi_Host *host; struct i2o_device *i2o_dev; - struct device *dev; int tid; struct i2o_message __iomem *msg; u32 m; - u32 scsi_flags, sg_flags; + /* + * ENABLE_DISCONNECT + * SIMPLE_TAG + * RETURN_SENSE_DATA_IN_REPLY_MESSAGE_FRAME + */ + u32 scsi_flags = 0x20a00000; + u32 sgl_offset; u32 __iomem *mptr; - u32 __iomem *lenptr; - u32 len, reqlen; - int i; + u32 cmd = I2O_CMD_SCSI_EXEC << 24; + int rc = 0; /* * Do the incoming paperwork */ - i2o_dev = SCpnt->device->hostdata; - host = SCpnt->device->host; c = i2o_dev->iop; - dev = &c->pdev->dev; SCpnt->scsi_done = done; @@ -562,7 +535,7 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, osm_warn("no I2O device in request\n"); SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); - return 0; + goto exit; } tid = i2o_dev->lct_data.tid; @@ -570,45 +543,86 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, osm_debug("qcmd: Tid = %03x\n", tid); osm_debug("Real scsi messages.\n"); - /* - * Obtain an I2O message. If there are none free then - * throw it back to the scsi layer - */ - - m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); - if (m == I2O_QUEUE_EMPTY) - return SCSI_MLQUEUE_HOST_BUSY; - /* * Put together a scsi execscb message */ - - len = SCpnt->request_bufflen; - switch (SCpnt->sc_data_direction) { case PCI_DMA_NONE: - scsi_flags = 0x00000000; // DATA NO XFER - sg_flags = 0x00000000; + /* DATA NO XFER */ + sgl_offset = SGL_OFFSET_0; break; case PCI_DMA_TODEVICE: - scsi_flags = 0x80000000; // DATA OUT (iop-->dev) - sg_flags = 0x14000000; + /* DATA OUT (iop-->dev) */ + scsi_flags |= 0x80000000; + sgl_offset = SGL_OFFSET_10; break; case PCI_DMA_FROMDEVICE: - scsi_flags = 0x40000000; // DATA IN (iop<--dev) - sg_flags = 0x10000000; + /* DATA IN (iop<--dev) */ + scsi_flags |= 0x40000000; + sgl_offset = SGL_OFFSET_10; break; default: /* Unknown - kill the command */ SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); - return 0; + goto exit; } - writel(I2O_CMD_SCSI_EXEC << 24 | HOST_TID << 12 | tid, &msg->u.head[1]); + /* + * Obtain an I2O message. If there are none free then + * throw it back to the scsi layer + */ + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) { + rc = SCSI_MLQUEUE_HOST_BUSY; + goto exit; + } + + mptr = &msg->body[0]; + +#ifdef CONFIG_I2O_EXT_ADAPTEC + if (c->adaptec) { + u32 adpt_flags = 0; + + if (SCpnt->sc_request && SCpnt->sc_request->upper_private_data) { + i2o_sg_io_hdr_t __user *usr_ptr = + ((Sg_request *) (SCpnt->sc_request-> + upper_private_data))->header. + usr_ptr; + + if (usr_ptr) + get_user(adpt_flags, &usr_ptr->flags); + } + + switch (i2o_dev->lct_data.class_id) { + case I2O_CLASS_EXECUTIVE: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + /* interpret flag has to be set for executive */ + adpt_flags ^= I2O_DPT_SG_FLAG_INTERPRET; + break; + + default: + break; + } + + /* + * for Adaptec controllers we use the PRIVATE command, because + * the normal SCSI EXEC doesn't support all SCSI commands on + * all controllers (for example READ CAPACITY). + */ + if (sgl_offset == SGL_OFFSET_10) + sgl_offset = SGL_OFFSET_12; + cmd = I2O_CMD_PRIVATE << 24; + writel(I2O_VENDOR_DPT << 16 | I2O_CMD_SCSI_EXEC, mptr++); + writel(adpt_flags | tid, mptr++); + } +#endif + + writel(cmd | HOST_TID << 12 | tid, &msg->u.head[1]); writel(i2o_scsi_driver.context, &msg->u.s.icntxt); /* We want the SCSI control block back */ @@ -626,7 +640,7 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, */ /* Attach tags to the devices */ - /* + /* FIXME: implement if(SCpnt->device->tagged_supported) { if(SCpnt->tag == HEAD_OF_QUEUE_TAG) scsi_flags |= 0x01000000; @@ -635,67 +649,35 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, } */ - /* Direction, disconnect ok, tag, CDBLen */ - writel(scsi_flags | 0x20200000 | SCpnt->cmd_len, &msg->body[0]); - - mptr = &msg->body[1]; + writel(scsi_flags | SCpnt->cmd_len, mptr++); /* 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 + if (sgl_offset != SGL_OFFSET_0) { + /* write size of data addressed by SGL */ + writel(SCpnt->request_bufflen, mptr++); - /* Now fill in the SGList and command */ - if (SCpnt->use_sg) { - struct scatterlist *sg; - int sg_count; - - sg = SCpnt->request_buffer; - len = 0; - - sg_count = dma_map_sg(dev, sg, SCpnt->use_sg, - SCpnt->sc_data_direction); - - if (unlikely(sg_count <= 0)) - return -ENOMEM; - - for (i = SCpnt->use_sg; i > 0; i--) { - if (i == 1) - sg_flags |= 0xC0000000; - writel(sg_flags | sg_dma_len(sg), mptr++); - writel(sg_dma_address(sg), mptr++); - len += sg_dma_len(sg); - sg++; + /* Now fill in the SGList and command */ + if (SCpnt->use_sg) { + if (!i2o_dma_map_sg(c, SCpnt->request_buffer, + SCpnt->use_sg, + SCpnt->sc_data_direction, &mptr)) + goto nomem; + } else { + SCpnt->SCp.dma_handle = + i2o_dma_map_single(c, SCpnt->request_buffer, + SCpnt->request_bufflen, + SCpnt->sc_data_direction, &mptr); + if (dma_mapping_error(SCpnt->SCp.dma_handle)) + goto nomem; } - - reqlen = mptr - &msg->u.head[0]; - writel(len, lenptr); - } else { - len = SCpnt->request_bufflen; - - writel(len, lenptr); - - if (len > 0) { - dma_addr_t dma_addr; - - dma_addr = dma_map_single(dev, SCpnt->request_buffer, - SCpnt->request_bufflen, - SCpnt->sc_data_direction); - if (!dma_addr) - return -ENOMEM; - - SCpnt->SCp.ptr = (void *)(unsigned long)dma_addr; - sg_flags |= 0xC0000000; - writel(sg_flags | SCpnt->request_bufflen, mptr++); - writel(dma_addr, mptr++); - } else - reqlen = 9; } /* Stick the headers on */ - writel(reqlen << 16 | SGL_OFFSET_10, &msg->u.head[0]); + writel(I2O_MESSAGE_SIZE(mptr - &msg->u.head[0]) | sgl_offset, + &msg->u.head[0]); /* Queue the message */ i2o_msg_post(c, m); @@ -703,6 +685,13 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, osm_debug("Issued %ld\n", SCpnt->serial_number); return 0; + + nomem: + rc = -ENOMEM; + i2o_msg_nop(c, m); + + exit: + return rc; }; /** diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index 50c8cedf7a2d..42f8b810d6e5 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -28,8 +28,10 @@ #include #include #include +#include "core.h" -#define OSM_VERSION "$Rev$" +#define OSM_NAME "i2o" +#define OSM_VERSION "1.288" #define OSM_DESCRIPTION "I2O subsystem" /* global I2O controller list */ @@ -43,20 +45,6 @@ static struct i2o_dma i2o_systab; static int i2o_hrt_get(struct i2o_controller *c); -/* Module internal functions from other sources */ -extern struct i2o_driver i2o_exec_driver; -extern int i2o_exec_lct_get(struct i2o_controller *); -extern void i2o_device_remove(struct i2o_device *); - -extern int __init i2o_driver_init(void); -extern void __exit i2o_driver_exit(void); -extern int __init i2o_exec_init(void); -extern void __exit i2o_exec_exit(void); -extern int __init i2o_pci_init(void); -extern void __exit i2o_pci_exit(void); -extern int i2o_device_init(void); -extern void i2o_device_exit(void); - /** * i2o_msg_nop - Returns a message which is not used * @c: I2O controller from which the message was created @@ -68,7 +56,7 @@ extern void i2o_device_exit(void); */ void i2o_msg_nop(struct i2o_controller *c, u32 m) { - struct i2o_message __iomem *msg = c->in_queue.virt + m; + struct i2o_message __iomem *msg = i2o_msg_in_to_virt(c, m); writel(THREE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); writel(I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | ADAPTER_TID, @@ -92,16 +80,16 @@ void i2o_msg_nop(struct i2o_controller *c, u32 m) * address from the read port (see the i2o spec). If no message is * available returns I2O_QUEUE_EMPTY and msg is leaved untouched. */ -u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message __iomem **msg, - int wait) +u32 i2o_msg_get_wait(struct i2o_controller *c, + struct i2o_message __iomem ** msg, int wait) { unsigned long timeout = jiffies + wait * HZ; u32 m; while ((m = i2o_msg_get(c, msg)) == I2O_QUEUE_EMPTY) { if (time_after(jiffies, timeout)) { - pr_debug("%s: Timeout waiting for message frame.\n", - c->name); + osm_debug("%s: Timeout waiting for message frame.\n", + c->name); return I2O_QUEUE_EMPTY; } set_current_state(TASK_UNINTERRUPTIBLE); @@ -129,13 +117,13 @@ u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr) unsigned long flags; if (!ptr) - printk(KERN_ERR "%s: couldn't add NULL pointer to context list!" - "\n", c->name); + osm_err("%s: couldn't add NULL pointer to context list!\n", + c->name); entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) { - printk(KERN_ERR "%s: Could not allocate memory for context " - "list element\n", c->name); + osm_err("%s: Could not allocate memory for context list element" + "\n", c->name); return 0; } @@ -154,7 +142,7 @@ u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr) spin_unlock_irqrestore(&c->context_list_lock, flags); - pr_debug("%s: Add context to list %p -> %d\n", c->name, ptr, context); + osm_debug("%s: Add context to list %p -> %d\n", c->name, ptr, context); return entry->context; }; @@ -186,11 +174,11 @@ u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr) spin_unlock_irqrestore(&c->context_list_lock, flags); if (!context) - printk(KERN_WARNING "%s: Could not remove nonexistent ptr " - "%p\n", c->name, ptr); + osm_warn("%s: Could not remove nonexistent ptr %p\n", c->name, + ptr); - pr_debug("%s: remove ptr from context list %d -> %p\n", c->name, - context, ptr); + osm_debug("%s: remove ptr from context list %d -> %p\n", c->name, + context, ptr); return context; }; @@ -220,11 +208,10 @@ void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context) spin_unlock_irqrestore(&c->context_list_lock, flags); if (!ptr) - printk(KERN_WARNING "%s: context id %d not found\n", c->name, - context); + osm_warn("%s: context id %d not found\n", c->name, context); - pr_debug("%s: get ptr from context list %d -> %p\n", c->name, context, - ptr); + osm_debug("%s: get ptr from context list %d -> %p\n", c->name, context, + ptr); return ptr; }; @@ -252,11 +239,11 @@ u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr) spin_unlock_irqrestore(&c->context_list_lock, flags); if (!context) - printk(KERN_WARNING "%s: Could not find nonexistent ptr " - "%p\n", c->name, ptr); + osm_warn("%s: Could not find nonexistent ptr %p\n", c->name, + ptr); - pr_debug("%s: get context id from context list %p -> %d\n", c->name, - ptr, context); + osm_debug("%s: get context id from context list %p -> %d\n", c->name, + ptr, context); return context; }; @@ -336,10 +323,9 @@ static int i2o_iop_quiesce(struct i2o_controller *c) /* Long timeout needed for quiesce if lots of devices */ if ((rc = i2o_msg_post_wait(c, m, 240))) - printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", - c->name, -rc); + osm_info("%s: Unable to quiesce (status=%#x).\n", c->name, -rc); else - pr_debug("%s: Quiesced.\n", c->name); + osm_debug("%s: Quiesced.\n", c->name); i2o_status_get(c); // Entered READY state @@ -377,10 +363,9 @@ static int i2o_iop_enable(struct i2o_controller *c) /* How long of a timeout do we need? */ if ((rc = i2o_msg_post_wait(c, m, 240))) - printk(KERN_ERR "%s: Could not enable (status=%#x).\n", - c->name, -rc); + osm_err("%s: Could not enable (status=%#x).\n", c->name, -rc); else - pr_debug("%s: Enabled.\n", c->name); + osm_debug("%s: Enabled.\n", c->name); i2o_status_get(c); // entered OPERATIONAL state @@ -444,19 +429,77 @@ static int i2o_iop_clear(struct i2o_controller *c) &msg->u.head[1]); if ((rc = i2o_msg_post_wait(c, m, 30))) - printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", - c->name, -rc); + osm_info("%s: Unable to clear (status=%#x).\n", c->name, -rc); else - pr_debug("%s: Cleared.\n", c->name); + osm_debug("%s: Cleared.\n", c->name); /* Enable all IOPs */ i2o_iop_enable_all(); - i2o_status_get(c); - return rc; } +/** + * i2o_iop_init_outbound_queue - setup the outbound message queue + * @c: I2O controller + * + * Clear and (re)initialize IOP's outbound queue and post the message + * frames to the IOP. + * + * Returns 0 on success or a negative errno code on failure. + */ +static int i2o_iop_init_outbound_queue(struct i2o_controller *c) +{ + volatile u8 *status = c->status.virt; + u32 m; + struct i2o_message __iomem *msg; + ulong timeout; + int i; + + osm_debug("%s: Initializing Outbound Queue...\n", c->name); + + memset(c->status.virt, 0, 4); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0x00000000, &msg->u.s.tcntxt); + writel(PAGE_SIZE, &msg->body[0]); + /* Outbound msg frame size in words and Initcode */ + writel(I2O_OUTBOUND_MSG_FRAME_SIZE << 16 | 0x80, &msg->body[1]); + writel(0xd0000004, &msg->body[2]); + writel(i2o_dma_low(c->status.phys), &msg->body[3]); + writel(i2o_dma_high(c->status.phys), &msg->body[4]); + + i2o_msg_post(c, m); + + timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ; + while (*status <= I2O_CMD_IN_PROGRESS) { + if (time_after(jiffies, timeout)) { + osm_warn("%s: Timeout Initializing\n", c->name); + return -ETIMEDOUT; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + m = c->out_queue.phys; + + /* Post frames */ + for (i = 0; i < I2O_MAX_OUTBOUND_MSG_FRAMES; i++) { + i2o_flush_reply(c, m); + udelay(1); /* Promise */ + m += I2O_OUTBOUND_MSG_FRAME_SIZE * sizeof(u32); + } + + return 0; +} + /** * i2o_iop_reset - reset an I2O controller * @c: controller to reset @@ -468,20 +511,20 @@ static int i2o_iop_clear(struct i2o_controller *c) */ static int i2o_iop_reset(struct i2o_controller *c) { - u8 *status = c->status.virt; + volatile u8 *status = c->status.virt; struct i2o_message __iomem *msg; u32 m; unsigned long timeout; i2o_status_block *sb = c->status_block.virt; int rc = 0; - pr_debug("%s: Resetting controller\n", c->name); + osm_debug("%s: Resetting controller\n", c->name); m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); if (m == I2O_QUEUE_EMPTY) return -ETIMEDOUT; - memset(status, 0, 8); + memset(c->status_block.virt, 0, 8); /* Quiesce all IOPs first */ i2o_iop_quiesce_all(); @@ -493,49 +536,43 @@ static int i2o_iop_reset(struct i2o_controller *c) writel(0, &msg->u.s.tcntxt); //FIXME: use reasonable transaction context writel(0, &msg->body[0]); writel(0, &msg->body[1]); - writel(i2o_ptr_low((void *)c->status.phys), &msg->body[2]); - writel(i2o_ptr_high((void *)c->status.phys), &msg->body[3]); + writel(i2o_dma_low(c->status.phys), &msg->body[2]); + writel(i2o_dma_high(c->status.phys), &msg->body[3]); i2o_msg_post(c, m); /* Wait for a reply */ timeout = jiffies + I2O_TIMEOUT_RESET * HZ; while (!*status) { - if (time_after(jiffies, timeout)) { - printk(KERN_ERR "%s: IOP reset timeout.\n", c->name); - rc = -ETIMEDOUT; - goto exit; - } - - /* Promise bug */ - if (status[1] || status[4]) { - *status = 0; + if (time_after(jiffies, timeout)) break; - } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - - rmb(); } - if (*status == I2O_CMD_IN_PROGRESS) { + switch (*status) { + case I2O_CMD_REJECTED: + osm_warn("%s: IOP reset rejected\n", c->name); + rc = -EPERM; + break; + + case 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. + * 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. */ - pr_debug("%s: Reset in progress, waiting for reboot...\n", - c->name); + osm_debug("%s: Reset in progress, waiting for reboot...\n", + c->name); m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET); while (m == I2O_QUEUE_EMPTY) { if (time_after(jiffies, timeout)) { - printk(KERN_ERR "%s: IOP reset timeout.\n", - c->name); + osm_err("%s: IOP reset timeout.\n", c->name); rc = -ETIMEDOUT; goto exit; } @@ -545,20 +582,27 @@ static int i2o_iop_reset(struct i2o_controller *c) m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET); } i2o_msg_nop(c, m); + + /* from here all quiesce commands are safe */ + c->no_quiesce = 0; + + /* verify if controller is in state RESET */ + i2o_status_get(c); + + if (!c->promise && (sb->iop_state != ADAPTER_STATE_RESET)) + osm_warn("%s: reset completed, but adapter not in RESET" + " state.\n", c->name); + else + osm_debug("%s: reset completed.\n", c->name); + + break; + + default: + osm_err("%s: IOP reset timeout.\n", c->name); + rc = -ETIMEDOUT; + break; } - /* from here all quiesce commands are safe */ - c->no_quiesce = 0; - - /* If IopReset was rejected or didn't perform reset, try IopClear */ - i2o_status_get(c); - if (*status == I2O_CMD_REJECTED || sb->iop_state != ADAPTER_STATE_RESET) { - printk(KERN_WARNING "%s: Reset rejected, trying to clear\n", - c->name); - i2o_iop_clear(c); - } else - pr_debug("%s: Reset completed.\n", c->name); - exit: /* Enable all IOPs */ i2o_iop_enable_all(); @@ -566,88 +610,6 @@ static int i2o_iop_reset(struct i2o_controller *c) return rc; }; -/** - * i2o_iop_init_outbound_queue - setup the outbound message queue - * @c: I2O controller - * - * Clear and (re)initialize IOP's outbound queue and post the message - * frames to the IOP. - * - * Returns 0 on success or a negative errno code on failure. - */ -static int i2o_iop_init_outbound_queue(struct i2o_controller *c) -{ - u8 *status = c->status.virt; - u32 m; - struct i2o_message __iomem *msg; - ulong timeout; - int i; - - pr_debug("%s: Initializing Outbound Queue...\n", c->name); - - memset(status, 0, 4); - - m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); - if (m == I2O_QUEUE_EMPTY) - return -ETIMEDOUT; - - writel(EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6, &msg->u.head[0]); - writel(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID, - &msg->u.head[1]); - writel(i2o_exec_driver.context, &msg->u.s.icntxt); - writel(0x0106, &msg->u.s.tcntxt); /* FIXME: why 0x0106, maybe in - Spec? */ - writel(PAGE_SIZE, &msg->body[0]); - writel(MSG_FRAME_SIZE << 16 | 0x80, &msg->body[1]); /* Outbound msg frame - size in words and Initcode */ - writel(0xd0000004, &msg->body[2]); - writel(i2o_ptr_low((void *)c->status.phys), &msg->body[3]); - writel(i2o_ptr_high((void *)c->status.phys), &msg->body[4]); - - i2o_msg_post(c, m); - - timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ; - while (*status <= I2O_CMD_IN_PROGRESS) { - if (time_after(jiffies, timeout)) { - printk(KERN_WARNING "%s: Timeout Initializing\n", - c->name); - return -ETIMEDOUT; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - rmb(); - } - - m = c->out_queue.phys; - - /* Post frames */ - for (i = 0; i < NMBR_MSG_FRAMES; i++) { - i2o_flush_reply(c, m); - udelay(1); /* Promise */ - m += MSG_FRAME_SIZE * 4; - } - - return 0; -} - -/** - * i2o_iop_send_nop - send a core NOP message - * @c: controller - * - * Send a no-operation message with a reply set to cause no - * action either. Needed for bringing up promise controllers. - */ -static int i2o_iop_send_nop(struct i2o_controller *c) -{ - struct i2o_message __iomem *msg; - u32 m = i2o_msg_get_wait(c, &msg, HZ); - if (m == I2O_QUEUE_EMPTY) - return -ETIMEDOUT; - i2o_msg_nop(c, m); - return 0; -} - /** * i2o_iop_activate - Bring controller up to HOLD * @c: controller @@ -659,78 +621,62 @@ static int i2o_iop_send_nop(struct i2o_controller *c) */ static int i2o_iop_activate(struct i2o_controller *c) { - struct pci_dev *i960 = NULL; i2o_status_block *sb = c->status_block.virt; int rc; - - if (c->promise) { - /* Beat up the hardware first of all */ - i960 = - pci_find_slot(c->pdev->bus->number, - PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0)); - if (i960) - pci_write_config_word(i960, 0x42, 0); - - /* Follow this sequence precisely or the controller - ceases to perform useful functions until reboot */ - if ((rc = i2o_iop_send_nop(c))) - return rc; - - if ((rc = i2o_iop_reset(c))) - return rc; - } + int state; /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ /* In READY state, Get status */ rc = i2o_status_get(c); if (rc) { - printk(KERN_INFO "%s: Unable to obtain status, " - "attempting a reset.\n", c->name); - if (i2o_iop_reset(c)) + osm_info("%s: Unable to obtain status, attempting a reset.\n", + c->name); + rc = i2o_iop_reset(c); + if (rc) return rc; } if (sb->i2o_version > I2OVER15) { - printk(KERN_ERR "%s: Not running version 1.5 of the I2O " - "Specification.\n", c->name); + osm_err("%s: Not running version 1.5 of the I2O Specification." + "\n", c->name); return -ENODEV; } switch (sb->iop_state) { case ADAPTER_STATE_FAULTED: - printk(KERN_CRIT "%s: hardware fault\n", c->name); - return -ENODEV; + osm_err("%s: hardware fault\n", c->name); + return -EFAULT; case ADAPTER_STATE_READY: case ADAPTER_STATE_OPERATIONAL: case ADAPTER_STATE_HOLD: case ADAPTER_STATE_FAILED: - pr_debug("%s: already running, trying to reset...\n", c->name); - if (i2o_iop_reset(c)) - return -ENODEV; + osm_debug("%s: already running, trying to reset...\n", c->name); + rc = i2o_iop_reset(c); + if (rc) + return rc; } + /* preserve state */ + state = sb->iop_state; + rc = i2o_iop_init_outbound_queue(c); if (rc) return rc; - if (c->promise) { - if ((rc = i2o_iop_send_nop(c))) - return rc; + /* if adapter was not in RESET state clear now */ + if (state != ADAPTER_STATE_RESET) + i2o_iop_clear(c); - if ((rc = i2o_status_get(c))) - return rc; + i2o_status_get(c); - if (i960) - pci_write_config_word(i960, 0x42, 0x3FF); + if (sb->iop_state != ADAPTER_STATE_HOLD) { + osm_err("%s: failed to bring IOP into HOLD state\n", c->name); + return -EIO; } - /* In HOLD state */ - - rc = i2o_hrt_get(c); - - return rc; + return i2o_hrt_get(c); }; /** @@ -756,20 +702,18 @@ static int i2o_iop_systab_set(struct i2o_controller *c) res->flags = IORESOURCE_MEM; res->start = 0; res->end = 0; - printk(KERN_INFO "%s: requires private memory resources.\n", - c->name); + osm_info("%s: requires private memory resources.\n", c->name); root = pci_find_parent_resource(c->pdev, res); if (root == NULL) - printk(KERN_WARNING "%s: Can't find parent resource!\n", - c->name); + osm_warn("%s: Can't find parent resource!\n", c->name); if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ NULL, NULL) >= 0) { c->mem_alloc = 1; sb->current_mem_size = 1 + res->end - res->start; sb->current_mem_base = res->start; - printk(KERN_INFO "%s: allocated %ld bytes of PCI memory" - " at 0x%08lX.\n", c->name, - 1 + res->end - res->start, res->start); + osm_info("%s: allocated %ld bytes of PCI memory at " + "0x%08lX.\n", c->name, + 1 + res->end - res->start, res->start); } } @@ -779,20 +723,18 @@ static int i2o_iop_systab_set(struct i2o_controller *c) res->flags = IORESOURCE_IO; res->start = 0; res->end = 0; - printk(KERN_INFO "%s: requires private memory resources.\n", - c->name); + osm_info("%s: requires private memory resources.\n", c->name); root = pci_find_parent_resource(c->pdev, res); if (root == NULL) - printk(KERN_WARNING "%s: Can't find parent resource!\n", - c->name); + osm_warn("%s: Can't find parent resource!\n", c->name); if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ NULL, NULL) >= 0) { c->io_alloc = 1; sb->current_io_size = 1 + res->end - res->start; sb->current_mem_base = res->start; - printk(KERN_INFO "%s: allocated %ld bytes of PCI I/O at" - " 0x%08lX.\n", c->name, - 1 + res->end - res->start, res->start); + osm_info("%s: allocated %ld bytes of PCI I/O at 0x%08lX" + ".\n", c->name, 1 + res->end - res->start, + res->start); } } @@ -836,10 +778,10 @@ static int i2o_iop_systab_set(struct i2o_controller *c) PCI_DMA_TODEVICE); if (rc < 0) - printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", - c->name, -rc); + osm_err("%s: Unable to set SysTab (status=%#x).\n", c->name, + -rc); else - pr_debug("%s: SysTab set.\n", c->name); + osm_debug("%s: SysTab set.\n", c->name); i2o_status_get(c); // Entered READY state @@ -863,7 +805,7 @@ static int i2o_iop_online(struct i2o_controller *c) return rc; /* In READY state */ - pr_debug("%s: Attempting to enable...\n", c->name); + osm_debug("%s: Attempting to enable...\n", c->name); rc = i2o_iop_enable(c); if (rc) return rc; @@ -882,7 +824,7 @@ void i2o_iop_remove(struct i2o_controller *c) { struct i2o_device *dev, *tmp; - pr_debug("%s: deleting controller\n", c->name); + osm_debug("%s: deleting controller\n", c->name); i2o_driver_notify_controller_remove_all(c); @@ -891,8 +833,12 @@ void i2o_iop_remove(struct i2o_controller *c) list_for_each_entry_safe(dev, tmp, &c->devices, list) i2o_device_remove(dev); + device_del(&c->device); + /* Ask the IOP to switch to RESET state */ i2o_iop_reset(c); + + put_device(&c->device); } /** @@ -927,8 +873,7 @@ static int i2o_systab_build(void) systab = i2o_systab.virt = kmalloc(i2o_systab.len, GFP_KERNEL); if (!systab) { - printk(KERN_ERR "i2o: unable to allocate memory for System " - "Table\n"); + osm_err("unable to allocate memory for System Table\n"); return -ENOMEM; } memset(systab, 0, i2o_systab.len); @@ -940,8 +885,8 @@ static int i2o_systab_build(void) i2o_status_block *sb; if (count >= num_controllers) { - printk(KERN_ERR "i2o: controller added while building " - "system table\n"); + osm_err("controller added while building system table" + "\n"); break; } @@ -955,9 +900,8 @@ static int i2o_systab_build(void) * it is techninically not part of the I2O subsystem... */ if (unlikely(i2o_status_get(c))) { - printk(KERN_ERR "%s: Deleting b/c could not get status" - " while attempting to build system table\n", - c->name); + osm_err("%s: Deleting b/c could not get status while " + "attempting to build system table\n", c->name); i2o_iop_remove(c); continue; // try the next one } @@ -971,8 +915,10 @@ static int i2o_systab_build(void) systab->iops[count].frame_size = sb->inbound_frame_size; systab->iops[count].last_changed = change_ind; systab->iops[count].iop_capabilities = sb->iop_capabilities; - systab->iops[count].inbound_low = i2o_ptr_low(c->post_port); - systab->iops[count].inbound_high = i2o_ptr_high(c->post_port); + systab->iops[count].inbound_low = + i2o_dma_low(c->base.phys + I2O_IN_PORT); + systab->iops[count].inbound_high = + i2o_dma_high(c->base.phys + I2O_IN_PORT); count++; } @@ -1010,11 +956,11 @@ int i2o_status_get(struct i2o_controller *c) { struct i2o_message __iomem *msg; u32 m; - u8 *status_block; + volatile u8 *status_block; unsigned long timeout; status_block = (u8 *) c->status_block.virt; - memset(status_block, 0, sizeof(i2o_status_block)); + memset(c->status_block.virt, 0, sizeof(i2o_status_block)); m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); if (m == I2O_QUEUE_EMPTY) @@ -1027,8 +973,8 @@ int i2o_status_get(struct i2o_controller *c) writel(0, &msg->u.s.tcntxt); // FIXME: use resonable transaction context writel(0, &msg->body[0]); writel(0, &msg->body[1]); - writel(i2o_ptr_low((void *)c->status_block.phys), &msg->body[2]); - writel(i2o_ptr_high((void *)c->status_block.phys), &msg->body[3]); + writel(i2o_dma_low(c->status_block.phys), &msg->body[2]); + writel(i2o_dma_high(c->status_block.phys), &msg->body[3]); writel(sizeof(i2o_status_block), &msg->body[4]); /* always 88 bytes */ i2o_msg_post(c, m); @@ -1037,14 +983,12 @@ int i2o_status_get(struct i2o_controller *c) timeout = jiffies + I2O_TIMEOUT_STATUS_GET * HZ; while (status_block[87] != 0xFF) { if (time_after(jiffies, timeout)) { - printk(KERN_ERR "%s: Get status timeout.\n", c->name); + osm_err("%s: Get status timeout.\n", c->name); return -ETIMEDOUT; } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - - rmb(); } #ifdef DEBUG @@ -1088,8 +1032,8 @@ static int i2o_hrt_get(struct i2o_controller *c) rc = i2o_msg_post_wait_mem(c, m, 20, &c->hrt); if (rc < 0) { - printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", - c->name, -rc); + osm_err("%s: Unable to get HRT (status=%#x)\n", c->name, + -rc); return rc; } @@ -1103,12 +1047,40 @@ static int i2o_hrt_get(struct i2o_controller *c) return i2o_parse_hrt(c); } - printk(KERN_ERR "%s: Unable to get HRT after %d tries, giving up\n", - c->name, I2O_HRT_GET_TRIES); + osm_err("%s: Unable to get HRT after %d tries, giving up\n", c->name, + I2O_HRT_GET_TRIES); return -EBUSY; } +/** + * i2o_iop_free - Free the i2o_controller struct + * @c: I2O controller to free + */ +void i2o_iop_free(struct i2o_controller *c) +{ + kfree(c); +}; + +/** + * i2o_iop_release - release the memory for a I2O controller + * @dev: I2O controller which should be released + * + * Release the allocated memory. This function is called if refcount of + * device reaches 0 automatically. + */ +static void i2o_iop_release(struct device *dev) +{ + struct i2o_controller *c = to_i2o_controller(dev); + + i2o_iop_free(c); +}; + +/* I2O controller class */ +static struct class i2o_controller_class = { + .name = "i2o_controller", +}; + /** * i2o_iop_alloc - Allocate and initialize a i2o_controller struct * @@ -1125,8 +1097,8 @@ struct i2o_controller *i2o_iop_alloc(void) c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) { - printk(KERN_ERR "i2o: Insufficient memory to allocate a I2O " - "controller.\n"); + osm_err("i2o: Insufficient memory to allocate a I2O controller." + "\n"); return ERR_PTR(-ENOMEM); } memset(c, 0, sizeof(*c)); @@ -1137,6 +1109,16 @@ struct i2o_controller *i2o_iop_alloc(void) c->unit = unit++; sprintf(c->name, "iop%d", c->unit); + device_initialize(&c->device); + class_device_initialize(&c->classdev); + + c->device.release = &i2o_iop_release; + c->classdev.class = &i2o_controller_class; + c->classdev.dev = &c->device; + + snprintf(c->device.bus_id, BUS_ID_SIZE, "iop%d", c->unit); + snprintf(c->classdev.class_id, BUS_ID_SIZE, "iop%d", c->unit); + #if BITS_PER_LONG == 64 spin_lock_init(&c->context_list_lock); atomic_set(&c->context_list_counter, 0); @@ -1146,15 +1128,6 @@ struct i2o_controller *i2o_iop_alloc(void) return c; }; -/** - * i2o_iop_free - Free the i2o_controller struct - * @c: I2O controller to free - */ -void i2o_iop_free(struct i2o_controller *c) -{ - kfree(c); -}; - /** * i2o_iop_add - Initialize the I2O controller and add him to the I2O core * @c: controller @@ -1168,45 +1141,58 @@ int i2o_iop_add(struct i2o_controller *c) { int rc; - printk(KERN_INFO "%s: Activating I2O controller...\n", c->name); - printk(KERN_INFO "%s: This may take a few minutes if there are many " - "devices\n", c->name); + if ((rc = device_add(&c->device))) { + osm_err("%s: could not add controller\n", c->name); + goto iop_reset; + } + + if ((rc = class_device_add(&c->classdev))) { + osm_err("%s: could not add controller class\n", c->name); + goto device_del; + } + + osm_info("%s: Activating I2O controller...\n", c->name); + osm_info("%s: This may take a few minutes if there are many devices\n", + c->name); if ((rc = i2o_iop_activate(c))) { - printk(KERN_ERR "%s: could not activate controller\n", - c->name); - i2o_iop_reset(c); - return rc; + osm_err("%s: could not activate controller\n", c->name); + goto class_del; } - pr_debug("%s: building sys table...\n", c->name); + osm_debug("%s: building sys table...\n", c->name); - if ((rc = i2o_systab_build())) { - i2o_iop_reset(c); - return rc; - } + if ((rc = i2o_systab_build())) + goto class_del; - pr_debug("%s: online controller...\n", c->name); + osm_debug("%s: online controller...\n", c->name); - if ((rc = i2o_iop_online(c))) { - i2o_iop_reset(c); - return rc; - } + if ((rc = i2o_iop_online(c))) + goto class_del; - pr_debug("%s: getting LCT...\n", c->name); + osm_debug("%s: getting LCT...\n", c->name); - if ((rc = i2o_exec_lct_get(c))) { - i2o_iop_reset(c); - return rc; - } + if ((rc = i2o_exec_lct_get(c))) + goto class_del; list_add(&c->list, &i2o_controllers); i2o_driver_notify_controller_add_all(c); - printk(KERN_INFO "%s: Controller added\n", c->name); + osm_info("%s: Controller added\n", c->name); return 0; + + class_del: + class_device_del(&c->classdev); + + device_del: + device_del(&c->device); + + iop_reset: + i2o_iop_reset(c); + + return rc; }; /** @@ -1264,16 +1250,18 @@ static int __init i2o_iop_init(void) if (rc) goto exit; - rc = i2o_driver_init(); - if (rc) + if ((rc = class_register(&i2o_controller_class))) { + osm_err("can't register class i2o_controller\n"); goto device_exit; + } - rc = i2o_exec_init(); - if (rc) + if ((rc = i2o_driver_init())) + goto class_exit; + + if ((rc = i2o_exec_init())) goto driver_exit; - rc = i2o_pci_init(); - if (rc < 0) + if ((rc = i2o_pci_init())) goto exec_exit; return 0; @@ -1284,6 +1272,9 @@ static int __init i2o_iop_init(void) driver_exit: i2o_driver_exit(); + class_exit: + class_unregister(&i2o_controller_class); + device_exit: i2o_device_exit(); @@ -1301,6 +1292,7 @@ static void __exit i2o_iop_exit(void) i2o_pci_exit(); i2o_exec_exit(); i2o_driver_exit(); + class_unregister(&i2o_controller_class); i2o_device_exit(); }; diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c index e772752f056d..7a60fd7be8ad 100644 --- a/drivers/message/i2o/pci.c +++ b/drivers/message/i2o/pci.c @@ -30,52 +30,17 @@ #include #include #include - -#ifdef CONFIG_MTRR -#include -#endif // CONFIG_MTRR - -/* Module internal functions from other sources */ -extern struct i2o_controller *i2o_iop_alloc(void); -extern void i2o_iop_free(struct i2o_controller *); - -extern int i2o_iop_add(struct i2o_controller *); -extern void i2o_iop_remove(struct i2o_controller *); - -extern int i2o_driver_dispatch(struct i2o_controller *, u32, - struct i2o_message *); +#include "core.h" /* PCI device id table for all I2O controllers */ static struct pci_device_id __devinitdata i2o_pci_ids[] = { {PCI_DEVICE_CLASS(PCI_CLASS_INTELLIGENT_I2O << 8, 0xffff00)}, {PCI_DEVICE(PCI_VENDOR_ID_DPT, 0xa511)}, + {.vendor = PCI_VENDOR_ID_INTEL,.device = 0x1962, + .subvendor = PCI_VENDOR_ID_PROMISE,.subdevice = PCI_ANY_ID}, {0} }; -/** - * i2o_dma_realloc - Realloc DMA memory - * @dev: struct device pointer to the PCI device of the I2O controller - * @addr: pointer to a i2o_dma struct DMA buffer - * @len: new length of memory - * @gfp_mask: GFP mask - * - * If there was something allocated in the addr, free it first. If len > 0 - * than try to allocate it and write the addresses back to the addr - * structure. If len == 0 set the virtual address to NULL. - * - * Returns the 0 on success or negative error code on failure. - */ -int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, size_t len, - unsigned int gfp_mask) -{ - i2o_dma_free(dev, addr); - - if (len) - return i2o_dma_alloc(dev, addr, len, gfp_mask); - - return 0; -}; - /** * i2o_pci_free - Frees the DMA memory for the I2O controller * @c: I2O controller to free @@ -91,19 +56,11 @@ static void i2o_pci_free(struct i2o_controller *c) i2o_dma_free(dev, &c->out_queue); i2o_dma_free(dev, &c->status_block); - if (c->lct) - kfree(c->lct); + kfree(c->lct); i2o_dma_free(dev, &c->dlct); i2o_dma_free(dev, &c->hrt); i2o_dma_free(dev, &c->status); -#ifdef CONFIG_MTRR - if (c->mtrr_reg0 >= 0) - mtrr_del(c->mtrr_reg0, 0, 0); - if (c->mtrr_reg1 >= 0) - mtrr_del(c->mtrr_reg1, 0, 0); -#endif - if (c->raptor && c->in_queue.virt) iounmap(c->in_queue.virt); @@ -178,14 +135,15 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c) c->name, (unsigned long)c->base.phys, (unsigned long)c->base.len); - c->base.virt = ioremap(c->base.phys, c->base.len); + c->base.virt = ioremap_nocache(c->base.phys, c->base.len); if (!c->base.virt) { printk(KERN_ERR "%s: Unable to map controller.\n", c->name); return -ENOMEM; } if (c->raptor) { - c->in_queue.virt = ioremap(c->in_queue.phys, c->in_queue.len); + c->in_queue.virt = + ioremap_nocache(c->in_queue.phys, c->in_queue.len); if (!c->in_queue.virt) { printk(KERN_ERR "%s: Unable to map controller.\n", c->name); @@ -195,43 +153,10 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c) } else c->in_queue = c->base; - c->irq_mask = c->base.virt + 0x34; - c->post_port = c->base.virt + 0x40; - c->reply_port = c->base.virt + 0x44; - -#ifdef CONFIG_MTRR - /* Enable Write Combining MTRR for IOP's memory region */ - c->mtrr_reg0 = mtrr_add(c->in_queue.phys, c->in_queue.len, - MTRR_TYPE_WRCOMB, 1); - c->mtrr_reg1 = -1; - - if (c->mtrr_reg0 < 0) - printk(KERN_WARNING "%s: could not enable write combining " - "MTRR\n", c->name); - else - printk(KERN_INFO "%s: using write combining MTRR\n", c->name); - - /* - * 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. - */ - if ((pdev->vendor == PCI_VENDOR_ID_INTEL || - pdev->vendor == PCI_VENDOR_ID_DPT) && !c->raptor) { - printk(KERN_INFO "%s: MTRR workaround for Intel i960 processor" - "\n", c->name); - c->mtrr_reg1 = mtrr_add(c->base.phys, 0x10000, - MTRR_TYPE_UNCACHABLE, 1); - - if (c->mtrr_reg1 < 0) { - printk(KERN_WARNING "%s: Error in setting " - "MTRR_TYPE_UNCACHABLE\n", c->name); - mtrr_del(c->mtrr_reg0, c->in_queue.phys, - c->in_queue.len); - c->mtrr_reg0 = -1; - } - } -#endif + c->irq_status = c->base.virt + I2O_IRQ_STATUS; + c->irq_mask = c->base.virt + I2O_IRQ_MASK; + c->in_port = c->base.virt + I2O_IN_PORT; + c->out_port = c->base.virt + I2O_OUT_PORT; if (i2o_dma_alloc(dev, &c->status, 8, GFP_KERNEL)) { i2o_pci_free(c); @@ -254,7 +179,10 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c) return -ENOMEM; } - if (i2o_dma_alloc(dev, &c->out_queue, MSG_POOL_SIZE, GFP_KERNEL)) { + if (i2o_dma_alloc + (dev, &c->out_queue, + I2O_MAX_OUTBOUND_MSG_FRAMES * I2O_OUTBOUND_MSG_FRAME_SIZE * + sizeof(u32), GFP_KERNEL)) { i2o_pci_free(c); return -ENOMEM; } @@ -276,51 +204,30 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c) static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) { struct i2o_controller *c = dev_id; - struct device *dev = &c->pdev->dev; - struct i2o_message *m; - u32 mv; + u32 m; + irqreturn_t rc = IRQ_NONE; - /* - * Old 960 steppings had a bug in the I2O unit that caused - * the queue to appear empty when it wasn't. - */ - mv = I2O_REPLY_READ32(c); - if (mv == I2O_QUEUE_EMPTY) { - mv = I2O_REPLY_READ32(c); - if (unlikely(mv == I2O_QUEUE_EMPTY)) { - return IRQ_NONE; - } else - pr_debug("%s: 960 bug detected\n", c->name); - } - - while (mv != I2O_QUEUE_EMPTY) { - /* - * Map the message from the page frame map to kernel virtual. - * Because bus_to_virt is deprecated, we have calculate the - * location by ourself! - */ - m = i2o_msg_out_to_virt(c, mv); - - /* - * Ensure this message is seen coherently but cachably by - * the processor - */ - dma_sync_single_for_cpu(dev, mv, MSG_FRAME_SIZE * 4, - PCI_DMA_FROMDEVICE); + while (readl(c->irq_status) & I2O_IRQ_OUTBOUND_POST) { + m = readl(c->out_port); + if (m == I2O_QUEUE_EMPTY) { + /* + * Old 960 steppings had a bug in the I2O unit that + * caused the queue to appear empty when it wasn't. + */ + m = readl(c->out_port); + if (unlikely(m == I2O_QUEUE_EMPTY)) + break; + } /* dispatch it */ - if (i2o_driver_dispatch(c, mv, m)) + if (i2o_driver_dispatch(c, m)) /* flush it if result != 0 */ - i2o_flush_reply(c, mv); + i2o_flush_reply(c, m); - /* - * That 960 bug again... - */ - mv = I2O_REPLY_READ32(c); - if (mv == I2O_QUEUE_EMPTY) - mv = I2O_REPLY_READ32(c); + rc = IRQ_HANDLED; } - return IRQ_HANDLED; + + return rc; } /** @@ -336,7 +243,7 @@ static int i2o_pci_irq_enable(struct i2o_controller *c) struct pci_dev *pdev = c->pdev; int rc; - I2O_IRQ_WRITE32(c, 0xffffffff); + writel(0xffffffff, c->irq_mask); if (pdev->irq) { rc = request_irq(pdev->irq, i2o_pci_interrupt, SA_SHIRQ, @@ -348,7 +255,7 @@ static int i2o_pci_irq_enable(struct i2o_controller *c) } } - I2O_IRQ_WRITE32(c, 0x00000000); + writel(0x00000000, c->irq_mask); printk(KERN_INFO "%s: Installed at IRQ %d\n", c->name, pdev->irq); @@ -363,7 +270,7 @@ static int i2o_pci_irq_enable(struct i2o_controller *c) */ static void i2o_pci_irq_disable(struct i2o_controller *c) { - I2O_IRQ_WRITE32(c, 0xffffffff); + writel(0xffffffff, c->irq_mask); if (c->pdev->irq > 0) free_irq(c->pdev->irq, c); @@ -385,28 +292,25 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev, { struct i2o_controller *c; int rc; + struct pci_dev *i960 = NULL; printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); if ((pdev->class & 0xff) > 1) { - printk(KERN_WARNING "i2o: I2O controller found but does not " - "support I2O 1.5 (skipping).\n"); + printk(KERN_WARNING "i2o: %s does not support I2O 1.5 " + "(skipping).\n", pci_name(pdev)); return -ENODEV; } if ((rc = pci_enable_device(pdev))) { - printk(KERN_WARNING "i2o: I2O controller found but could not be" - " enabled.\n"); + printk(KERN_WARNING "i2o: couldn't enable device %s\n", + pci_name(pdev)); return rc; } - printk(KERN_INFO "i2o: I2O controller found on bus %d at %d.\n", - pdev->bus->number, pdev->devfn); - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { - printk(KERN_WARNING "i2o: I2O controller on bus %d at %d: No " - "suitable DMA available!\n", pdev->bus->number, - pdev->devfn); + printk(KERN_WARNING "i2o: no suitable DMA found for %s\n", + pci_name(pdev)); rc = -ENODEV; goto disable; } @@ -415,14 +319,16 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev, c = i2o_iop_alloc(); if (IS_ERR(c)) { - printk(KERN_ERR "i2o: memory for I2O controller could not be " - "allocated\n"); + printk(KERN_ERR "i2o: couldn't allocate memory for %s\n", + pci_name(pdev)); rc = PTR_ERR(c); goto disable; - } + } else + printk(KERN_INFO "%s: controller found (%s)\n", c->name, + pci_name(pdev)); c->pdev = pdev; - c->device = pdev->dev; + c->device.parent = get_device(&pdev->dev); /* Cards that fall apart if you hit them with large I/O loads... */ if (pdev->vendor == PCI_VENDOR_ID_NCR && pdev->device == 0x0630) { @@ -432,16 +338,48 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev, } if (pdev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) { + /* + * Expose the ship behind i960 for initialization, or it will + * failed + */ + i960 = + pci_find_slot(c->pdev->bus->number, + PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0)); + + if (i960) + pci_write_config_word(i960, 0x42, 0); + c->promise = 1; - printk(KERN_INFO "%s: Promise workarounds activated.\n", - c->name); + c->limit_sectors = 1; } + if (pdev->subsystem_vendor == PCI_VENDOR_ID_DPT) + c->adaptec = 1; + /* Cards that go bananas if you quiesce them before you reset them. */ if (pdev->vendor == PCI_VENDOR_ID_DPT) { c->no_quiesce = 1; if (pdev->device == 0xa511) c->raptor = 1; + + if (pdev->subsystem_device == 0xc05a) { + c->limit_sectors = 1; + printk(KERN_INFO + "%s: limit sectors per request to %d\n", c->name, + I2O_MAX_SECTORS_LIMITED); + } +#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 + if (sizeof(dma_addr_t) > 4) { + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK)) + printk(KERN_INFO "%s: 64-bit DMA unavailable\n", + c->name); + else { + c->pae_support = 1; + printk(KERN_INFO "%s: using 64-bit DMA\n", + c->name); + } + } +#endif } if ((rc = i2o_pci_alloc(c))) { @@ -459,6 +397,11 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev, if ((rc = i2o_iop_add(c))) goto uninstall; + get_device(&c->device); + + if (i960) + pci_write_config_word(i960, 0x42, 0x03ff); + return 0; uninstall: @@ -469,6 +412,7 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev, free_controller: i2o_iop_free(c); + put_device(c->device.parent); disable: pci_disable_device(pdev); @@ -492,15 +436,17 @@ static void __devexit i2o_pci_remove(struct pci_dev *pdev) i2o_pci_irq_disable(c); i2o_pci_free(c); + pci_disable_device(pdev); + printk(KERN_INFO "%s: Controller removed.\n", c->name); - i2o_iop_free(c); - pci_disable_device(pdev); + put_device(c->device.parent); + put_device(&c->device); }; /* PCI driver for I2O controller */ static struct pci_driver i2o_pci_driver = { - .name = "I2O controller", + .name = "PCI_I2O", .id_table = i2o_pci_ids, .probe = i2o_pci_probe, .remove = __devexit_p(i2o_pci_remove), @@ -523,6 +469,4 @@ void __exit i2o_pci_exit(void) { pci_unregister_driver(&i2o_pci_driver); }; - -EXPORT_SYMBOL(i2o_dma_realloc); MODULE_DEVICE_TABLE(pci, i2o_pci_ids); diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 325495b8b60c..137226d98d47 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -2307,6 +2307,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) tso = e1000_tso(adapter, skb); if (tso < 0) { dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&adapter->tx_lock, flags); return NETDEV_TX_OK; } diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 89454915b857..e44f8e9055ef 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -848,7 +848,7 @@ static void __exit sixpack_exit_driver(void) { int ret; - if ((ret = tty_register_ldisc(N_6PACK, NULL))) + if ((ret = tty_unregister_ldisc(N_6PACK))) printk(msg_unregfail, ret); } diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 62790511098f..3035422f5ad8 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -934,7 +934,7 @@ static void __exit mkiss_exit_driver(void) kfree(ax25_ctrls); ax25_ctrls = NULL; - if ((i = tty_register_ldisc(N_AX25, NULL))) + if ((i = tty_unregister_ldisc(N_AX25))) printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i); } diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index 7d23aa375908..b8d112348ba4 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -626,7 +626,7 @@ static void __exit irtty_sir_cleanup(void) { int err; - if ((err = tty_register_ldisc(N_IRDA, NULL))) { + if ((err = tty_unregister_ldisc(N_IRDA))) { IRDA_ERROR("%s(), can't unregister line discipline (err = %d)\n", __FUNCTION__, err); } diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 33b9d79b1aad..5e48b9ab3045 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -1025,7 +1025,7 @@ static void async_lcp_peek(struct asyncppp *ap, unsigned char *data, static void __exit ppp_async_cleanup(void) { - if (tty_register_ldisc(N_PPP, NULL) != 0) + if (tty_unregister_ldisc(N_PPP) != 0) printk(KERN_ERR "failed to unregister PPP line discipline\n"); } diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 7d0150b4c629..fd9f50180355 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -793,7 +793,7 @@ err: static void __exit ppp_sync_cleanup(void) { - if (tty_register_ldisc(N_SYNC_PPP, NULL) != 0) + if (tty_unregister_ldisc(N_SYNC_PPP) != 0) printk(KERN_ERR "failed to unregister Sync PPP line discipline\n"); } diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 8f7841c0374d..c79e0ad4ba02 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -198,18 +198,12 @@ err_exit: static void sl_free_bufs(struct slip *sl) { - void * tmp; - /* Free all SLIP frame buffers. */ - tmp = xchg(&sl->rbuff, NULL); - kfree(tmp); - tmp = xchg(&sl->xbuff, NULL); - kfree(tmp); + kfree(xchg(&sl->rbuff, NULL)); + kfree(xchg(&sl->xbuff, NULL)); #ifdef SL_INCLUDE_CSLIP - tmp = xchg(&sl->cbuff, NULL); - kfree(tmp); - if ((tmp = xchg(&sl->slcomp, NULL)) != NULL) - slhc_free(tmp); + kfree(xchg(&sl->cbuff, NULL)); + slhc_free(xchg(&sl->slcomp, NULL)); #endif } @@ -1430,7 +1424,7 @@ static void __exit slip_exit(void) kfree(slip_devs); slip_devs = NULL; - if ((i = tty_register_ldisc(N_SLIP, NULL))) + if ((i = tty_unregister_ldisc(N_SLIP))) { printk(KERN_ERR "SLIP: can't unregister line discipline (err = %d)\n", i); } diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 1c540d825551..bdf672c48182 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -829,7 +829,7 @@ static void __exit exit_x25_asy(void) } kfree(x25_asy_devs); - tty_register_ldisc(N_X25, NULL); + tty_unregister_ldisc(N_X25); } module_init(init_x25_asy); diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index ec8cf29ffced..6c42b573a95a 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -2828,7 +2828,7 @@ static void __exit strip_exit_driver(void) /* Unregister with the /proc/net file here. */ proc_net_remove("strip"); - if ((i = tty_register_ldisc(N_STRIP, NULL))) + if ((i = tty_unregister_ldisc(N_STRIP))) printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i); printk(signoff); diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 745a14183634..531b07313141 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -206,7 +206,7 @@ static inline unsigned long fast_get_dcookie(struct dentry * dentry, */ static unsigned long get_exec_dcookie(struct mm_struct * mm) { - unsigned long cookie = 0; + unsigned long cookie = NO_COOKIE; struct vm_area_struct * vma; if (!mm) @@ -234,35 +234,42 @@ out: */ static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) { - unsigned long cookie = 0; + unsigned long cookie = NO_COOKIE; struct vm_area_struct * vma; for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { - if (!vma->vm_file) - continue; - if (addr < vma->vm_start || addr >= vma->vm_end) continue; - cookie = fast_get_dcookie(vma->vm_file->f_dentry, - vma->vm_file->f_vfsmnt); - *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + if (vma->vm_file) { + cookie = fast_get_dcookie(vma->vm_file->f_dentry, + vma->vm_file->f_vfsmnt); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - + vma->vm_start; + } else { + /* must be an anonymous map */ + *offset = addr; + } + break; } + if (!vma) + cookie = INVALID_COOKIE; + return cookie; } -static unsigned long last_cookie = ~0UL; +static unsigned long last_cookie = INVALID_COOKIE; static void add_cpu_switch(int i) { add_event_entry(ESCAPE_CODE); add_event_entry(CPU_SWITCH_CODE); add_event_entry(i); - last_cookie = ~0UL; + last_cookie = INVALID_COOKIE; } static void add_kernel_ctx_switch(unsigned int in_kernel) @@ -317,7 +324,7 @@ static int add_us_sample(struct mm_struct * mm, struct op_sample * s) cookie = lookup_dcookie(mm, s->eip, &offset); - if (!cookie) { + if (cookie == INVALID_COOKIE) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); return 0; } diff --git a/drivers/oprofile/event_buffer.h b/drivers/oprofile/event_buffer.h index 442aaad391e0..018023630599 100644 --- a/drivers/oprofile/event_buffer.h +++ b/drivers/oprofile/event_buffer.h @@ -35,6 +35,9 @@ void wake_up_buffer_waiter(void); #define TRACE_BEGIN_CODE 8 #define TRACE_END_CODE 9 +#define INVALID_COOKIE ~0UL +#define NO_COOKIE 0UL + /* add data to the event buffer */ void add_event_entry(unsigned long data); diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index e7f3bcb79000..80edfa3abd29 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2751,7 +2751,6 @@ enum parport_pc_pci_cards { netmos_9755, netmos_9805, netmos_9815, - netmos_9855, }; @@ -2826,7 +2825,6 @@ static struct parport_pc_pci { /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ /* netmos_9805 */ { 1, { { 0, -1 }, } }, /* untested */ /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ - /* netmos_9855 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ }; static struct pci_device_id parport_pc_pci_tbl[] = { @@ -2907,8 +2905,6 @@ static struct pci_device_id parport_pc_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 6715a17b5d0f..00498e2f1205 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -34,6 +34,7 @@ enum parport_pc_pci_cards { titan_110l = 0, titan_210l, netmos_9xx5_combo, + netmos_9855, avlab_1s1p, avlab_1s1p_650, avlab_1s1p_850, @@ -87,6 +88,7 @@ static struct parport_pc_pci cards[] __devinitdata = { /* titan_110l */ { 1, { { 3, -1 }, } }, /* titan_210l */ { 1, { { 3, -1 }, } }, /* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init }, + /* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init }, /* avlab_1s1p */ { 1, { { 1, 2}, } }, /* avlab_1s1p_650 */ { 1, { { 1, 2}, } }, /* avlab_1s1p_850 */ { 1, { { 1, 2}, } }, @@ -120,7 +122,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = { { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ { 0x14db, 0x2110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p}, { 0x14db, 0x2111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p_650}, @@ -207,6 +209,7 @@ static struct pci_board_no_ids pci_boards[] __devinitdata = { /* titan_110l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 1, 921600 }, /* titan_210l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* netmos_9xx5_combo */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200, 0, 0, netmos_serial_init }, +/* netmos_9855 */ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200, 0, 0, netmos_serial_init }, /* avlab_1s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* avlab_1s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* avlab_1s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, diff --git a/drivers/parport/probe.c b/drivers/parport/probe.c index c94963145e17..6e6f42d01e64 100644 --- a/drivers/parport/probe.c +++ b/drivers/parport/probe.c @@ -48,14 +48,6 @@ static void pretty_print(struct parport *port, int device) printk("\n"); } -static char *strdup(char *str) -{ - int n = strlen(str)+1; - char *s = kmalloc(n, GFP_KERNEL); - if (!s) return NULL; - return strcpy(s, str); -} - static void parse_data(struct parport *port, int device, char *str) { char *txt = kmalloc(strlen(str)+1, GFP_KERNEL); @@ -88,16 +80,16 @@ static void parse_data(struct parport *port, int device, char *str) if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) { if (info->mfr) kfree (info->mfr); - info->mfr = strdup(sep); + info->mfr = kstrdup(sep, GFP_KERNEL); } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) { if (info->model) kfree (info->model); - info->model = strdup(sep); + info->model = kstrdup(sep, GFP_KERNEL); } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) { int i; if (info->class_name) kfree (info->class_name); - info->class_name = strdup(sep); + info->class_name = kstrdup(sep, GFP_KERNEL); for (u = sep; *u; u++) *u = toupper(*u); for (i = 0; classes[i].token; i++) { @@ -112,7 +104,7 @@ static void parse_data(struct parport *port, int device, char *str) !strcmp(p, "COMMAND SET")) { if (info->cmdset) kfree (info->cmdset); - info->cmdset = strdup(sep); + info->cmdset = kstrdup(sep, GFP_KERNEL); /* if it speaks printer language, it's probably a printer */ if (strstr(sep, "PJL") || strstr(sep, "PCL")) @@ -120,7 +112,7 @@ static void parse_data(struct parport *port, int device, char *str) } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) { if (info->description) kfree (info->description); - info->description = strdup(sep); + info->description = kstrdup(sep, GFP_KERNEL); } } rock_on: diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 03fc885db1c5..d136b3c8fac9 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -508,6 +508,10 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) cs_err(skt, "unsupported voltage key.\n"); return CS_BAD_TYPE; } + + if (skt->power_hook) + skt->power_hook(skt, HOOK_POWER_PRE); + skt->socket.flags = 0; skt->ops->set_socket(skt, &skt->socket); @@ -522,7 +526,12 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) return CS_BAD_TYPE; } - return socket_reset(skt); + status = socket_reset(skt); + + if (skt->power_hook) + skt->power_hook(skt, HOOK_POWER_POST); + + return status; } /* diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h index a8a1d104524a..c7ba99871aca 100644 --- a/drivers/pcmcia/ti113x.h +++ b/drivers/pcmcia/ti113x.h @@ -611,6 +611,170 @@ out: } } + +/* Returns true value if the second slot of a two-slot controller is empty */ +static int ti12xx_2nd_slot_empty(struct yenta_socket *socket) +{ + struct pci_dev *func; + struct yenta_socket *slot2; + int devfn; + unsigned int state; + int ret = 1; + + /* catch the two-slot controllers */ + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1220: + case PCI_DEVICE_ID_TI_1221: + case PCI_DEVICE_ID_TI_1225: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1420: + case PCI_DEVICE_ID_TI_1450: + case PCI_DEVICE_ID_TI_1451A: + case PCI_DEVICE_ID_TI_1520: + case PCI_DEVICE_ID_TI_1620: + case PCI_DEVICE_ID_TI_4520: + case PCI_DEVICE_ID_TI_4450: + case PCI_DEVICE_ID_TI_4451: + /* + * there are way more, but they need to be added in yenta_socket.c + * and pci_ids.h first anyway. + */ + break; + + /* single-slot controllers have the 2nd slot empty always :) */ + default: + return 1; + } + + /* get other slot */ + devfn = socket->dev->devfn & ~0x07; + func = pci_get_slot(socket->dev->bus, + (socket->dev->devfn & 0x07) ? devfn : devfn | 0x01); + if (!func) + return 1; + + slot2 = pci_get_drvdata(func); + if (!slot2) + goto out; + + /* check state */ + yenta_get_status(&socket->socket, &state); + if (state & SS_DETECT) { + ret = 0; + goto out; + } + +out: + pci_dev_put(func); + return ret; +} + +/* + * TI specifiy parts for the power hook. + * + * some TI's with some CB's produces interrupt storm on power on. it has been + * seen with atheros wlan cards on TI1225 and TI1410. solution is simply to + * disable any CB interrupts during this time. + */ +static int ti12xx_power_hook(struct pcmcia_socket *sock, int operation) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + u32 mfunc, devctl, sysctl; + u8 gpio3; + + /* only POWER_PRE and POWER_POST are interesting */ + if ((operation != HOOK_POWER_PRE) && (operation != HOOK_POWER_POST)) + return 0; + + devctl = config_readb(socket, TI113X_DEVICE_CONTROL); + sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL); + mfunc = config_readl(socket, TI122X_MFUNC); + + /* + * all serial/tied: only disable when modparm set. always doing it + * would mean a regression for working setups 'cos it disables the + * interrupts for both both slots on 2-slot controllers + * (and users of single slot controllers where it's save have to + * live with setting the modparm, most don't have to anyway) + */ + if (((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) && + (pwr_irqs_off || ti12xx_2nd_slot_empty(socket))) { + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1250: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + case PCI_DEVICE_ID_TI_1451A: + case PCI_DEVICE_ID_TI_4450: + case PCI_DEVICE_ID_TI_4451: + /* these chips have no IRQSER setting in MFUNC3 */ + break; + + default: + if (operation == HOOK_POWER_PRE) + mfunc = (mfunc & ~TI122X_MFUNC3_MASK); + else + mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER; + } + + return 0; + } + + /* do the job differently for func0/1 */ + if ((PCI_FUNC(socket->dev->devfn) == 0) || + ((sysctl & TI122X_SCR_INTRTIE) && + (pwr_irqs_off || ti12xx_2nd_slot_empty(socket)))) { + /* some bridges are different */ + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1250: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + /* those oldies use gpio3 for INTA */ + gpio3 = config_readb(socket, TI1250_GPIO3_CONTROL); + if (operation == HOOK_POWER_PRE) + gpio3 = (gpio3 & ~TI1250_GPIO_MODE_MASK) | 0x40; + else + gpio3 &= ~TI1250_GPIO_MODE_MASK; + config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3); + break; + + default: + /* all new bridges are the same */ + if (operation == HOOK_POWER_PRE) + mfunc &= ~TI122X_MFUNC0_MASK; + else + mfunc |= TI122X_MFUNC0_INTA; + config_writel(socket, TI122X_MFUNC, mfunc); + } + } else { + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + /* those have INTA elsewhere and INTB in MFUNC0 */ + if (operation == HOOK_POWER_PRE) + mfunc &= ~TI122X_MFUNC0_MASK; + else + mfunc |= TI125X_MFUNC0_INTB; + config_writel(socket, TI122X_MFUNC, mfunc); + + break; + + default: + /* all new bridges are the same */ + if (operation == HOOK_POWER_PRE) + mfunc &= ~TI122X_MFUNC1_MASK; + else + mfunc |= TI122X_MFUNC1_INTB; + config_writel(socket, TI122X_MFUNC, mfunc); + } + } + + return 0; +} + static int ti12xx_override(struct yenta_socket *socket) { u32 val, val_orig; @@ -654,6 +818,9 @@ static int ti12xx_override(struct yenta_socket *socket) else ti12xx_irqroute_func1(socket); + /* install power hook */ + socket->socket.power_hook = ti12xx_power_hook; + return ti_override(socket); } diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 6404d97a12eb..bee05362fd24 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -32,6 +32,14 @@ static int disable_clkrun; module_param(disable_clkrun, bool, 0444); MODULE_PARM_DESC(disable_clkrun, "If PC card doesn't function properly, please try this option"); +static int isa_probe = 1; +module_param(isa_probe, bool, 0444); +MODULE_PARM_DESC(isa_probe, "If set ISA interrupts are probed (default). Set to N to disable probing"); + +static int pwr_irqs_off; +module_param(pwr_irqs_off, bool, 0644); +MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!"); + #if 0 #define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args) #else @@ -150,15 +158,16 @@ static int yenta_get_status(struct pcmcia_socket *sock, unsigned int *value) val = (state & CB_3VCARD) ? SS_3VCARD : 0; val |= (state & CB_XVCARD) ? SS_XVCARD : 0; - val |= (state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD - | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING; + val |= (state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING; + val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? SS_PENDING : 0; + if (state & CB_CBCARD) { val |= SS_CARDBUS; val |= (state & CB_CARDSTS) ? SS_STSCHG : 0; val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? 0 : SS_DETECT; val |= (state & CB_PWRCYCLE) ? SS_POWERON | SS_READY : 0; - } else { + } else if (state & CB_16BITCARD) { u8 status = exca_readb(socket, I365_STATUS); val |= ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0; if (exca_readb(socket, I365_INTCTL) & I365_PC_IOCARD) { @@ -405,11 +414,13 @@ static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map * } -static unsigned int yenta_events(struct yenta_socket *socket) + +static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + unsigned int events; + struct yenta_socket *socket = (struct yenta_socket *) dev_id; u8 csc; u32 cb_event; - unsigned int events; /* Clear interrupt status for the event */ cb_event = cb_readl(socket, CB_SOCKET_EVENT); @@ -426,20 +437,13 @@ static unsigned int yenta_events(struct yenta_socket *socket) events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; events |= (csc & I365_CSC_READY) ? SS_READY : 0; } - return events; -} - -static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned int events; - struct yenta_socket *socket = (struct yenta_socket *) dev_id; - - events = yenta_events(socket); - if (events) { + if (events) pcmcia_parse_events(&socket->socket, events); + + if (cb_event || csc) return IRQ_HANDLED; - } + return IRQ_NONE; } @@ -470,11 +474,22 @@ static void yenta_clear_maps(struct yenta_socket *socket) } } +/* redoes voltage interrogation if required */ +static void yenta_interrogate(struct yenta_socket *socket) +{ + u32 state; + + state = cb_readl(socket, CB_SOCKET_STATE); + if (!(state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) || + (state & (CB_CDETECT1 | CB_CDETECT2 | CB_NOTACARD | CB_BADVCCREQ)) || + ((state & (CB_16BITCARD | CB_CBCARD)) == (CB_16BITCARD | CB_CBCARD))) + cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); +} + /* Called at resume and initialization events */ static int yenta_sock_init(struct pcmcia_socket *sock) { struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); - u32 state; u16 bridge; bridge = config_readw(socket, CB_BRIDGE_CONTROL) & ~CB_BRIDGE_INTR; @@ -486,10 +501,7 @@ static int yenta_sock_init(struct pcmcia_socket *sock) exca_writeb(socket, I365_GENCTL, 0x00); /* Redo card voltage interrogation */ - state = cb_readl(socket, CB_SOCKET_STATE); - if (!(state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | - CB_3VCARD | CB_XVCARD | CB_YVCARD))) - cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); + yenta_interrogate(socket); yenta_clear_maps(socket); @@ -856,7 +868,10 @@ static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_i socket->socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS; socket->socket.map_size = 0x1000; socket->socket.pci_irq = socket->cb_irq; - socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask); + if (isa_probe) + socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask); + else + socket->socket.irq_mask = 0; socket->socket.cb_dev = socket->dev; printk(KERN_INFO "Yenta: ISA IRQ mask 0x%04x, PCI irq %d\n", @@ -996,6 +1011,7 @@ static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_i } /* Figure out what the dang thing can do for the PCMCIA layer... */ + yenta_interrogate(socket); yenta_get_socket_capabilities(socket, isa_interrupts); printk(KERN_INFO "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE)); diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index 3252662958d3..add12f7c489a 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -259,7 +259,6 @@ int pnp_add_card_device(struct pnp_card * card, struct pnp_dev * dev) /** * pnp_remove_card_device- removes a device from the specified card - * @card: pointer to the card to remove from * @dev: pointer to the device to remove */ @@ -274,7 +273,7 @@ void pnp_remove_card_device(struct pnp_dev * dev) /** * pnp_request_card_device - Searches for a PnP device under the specified card - * @lcard: pointer to the card link, cannot be NULL + * @clink: pointer to the card link, cannot be NULL * @id: pointer to a PnP ID structure that explains the rules for finding the device * @from: Starting place to search from. If NULL it will start from the begining. */ diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 65ecef738537..6c510c19ad7d 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -390,6 +390,7 @@ fail: * pnp_manual_config_dev - Disables Auto Config and Manually sets the resource table * @dev: pointer to the desired device * @res: pointer to the new resource config + * @mode: 0 or PNP_CONFIG_FORCE * * This function can be used by drivers that want to manually set thier resources. */ diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 16ab8d363ac6..6bc27d52326f 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -35,14 +35,17 @@ static int dcssblk_open(struct inode *inode, struct file *filp); static int dcssblk_release(struct inode *inode, struct file *filp); static int dcssblk_make_request(struct request_queue *q, struct bio *bio); +static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, + unsigned long *data); static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; static int dcssblk_major; static struct block_device_operations dcssblk_devops = { - .owner = THIS_MODULE, - .open = dcssblk_open, - .release = dcssblk_release, + .owner = THIS_MODULE, + .open = dcssblk_open, + .release = dcssblk_release, + .direct_access = dcssblk_direct_access, }; static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, @@ -641,6 +644,20 @@ dcssblk_make_request(request_queue_t *q, struct bio *bio) /* Request beyond end of DCSS segment. */ goto fail; } + /* verify data transfer direction */ + if (dev_info->is_shared) { + switch (dev_info->segment_type) { + case SEG_TYPE_SR: + case SEG_TYPE_ER: + case SEG_TYPE_SC: + /* cannot write to these segments */ + if (bio_data_dir(bio) == WRITE) { + PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id); + goto fail; + } + } + } + index = (bio->bi_sector >> 3); bio_for_each_segment(bvec, bio, i) { page_addr = (unsigned long) @@ -661,7 +678,26 @@ dcssblk_make_request(request_queue_t *q, struct bio *bio) bio_endio(bio, bytes_done, 0); return 0; fail: - bio_io_error(bio, bytes_done); + bio_io_error(bio, bio->bi_size); + return 0; +} + +static int +dcssblk_direct_access (struct block_device *bdev, sector_t secnum, + unsigned long *data) +{ + struct dcssblk_dev_info *dev_info; + unsigned long pgoff; + + dev_info = bdev->bd_disk->private_data; + if (!dev_info) + return -ENODEV; + if (secnum % (PAGE_SIZE/512)) + return -EINVAL; + pgoff = secnum / (PAGE_SIZE / 512); + if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) + return -ERANGE; + *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE); return 0; } diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 9cc0015b717d..a699c30b2662 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -1126,11 +1126,11 @@ static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout) struct adpt_i2o_post_wait_data *p1, *p2; struct adpt_i2o_post_wait_data *wait_data = kmalloc(sizeof(struct adpt_i2o_post_wait_data),GFP_KERNEL); - adpt_wait_queue_t wait; + DECLARE_WAITQUEUE(wait, current); - if(!wait_data){ + if (!wait_data) return -ENOMEM; - } + /* * The spin locking is needed to keep anyone from playing * with the queue pointers and id while we do the same @@ -1148,12 +1148,7 @@ static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout) wait_data->wq = &adpt_wq_i2o_post; wait_data->status = -ETIMEDOUT; - // this code is taken from kernel/sched.c:interruptible_sleep_on_timeout - wait.task = current; - init_waitqueue_entry(&wait, current); - spin_lock_irqsave(&adpt_wq_i2o_post.lock, flags); - __add_wait_queue(&adpt_wq_i2o_post, &wait); - spin_unlock(&adpt_wq_i2o_post.lock); + add_wait_queue(&adpt_wq_i2o_post, &wait); msg[2] |= 0x80000000 | ((u32)wait_data->id); timeout *= HZ; @@ -1175,9 +1170,7 @@ static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout) if(pHba->host) spin_lock_irq(pHba->host->host_lock); } - spin_lock_irq(&adpt_wq_i2o_post.lock); - __remove_wait_queue(&adpt_wq_i2o_post, &wait); - spin_unlock_irqrestore(&adpt_wq_i2o_post.lock, flags); + remove_wait_queue(&adpt_wq_i2o_post, &wait); if(status == -ETIMEDOUT){ printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index ba347576d99b..d7a38b6713f9 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -56,7 +56,7 @@ static struct class shost_class = { * @shost: pointer to struct Scsi_Host * recovery: recovery requested to run. **/ -void scsi_host_cancel(struct Scsi_Host *shost, int recovery) +static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) { struct scsi_device *sdev; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 5578ae9a9e45..1cb5f7d4f278 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -68,6 +68,8 @@ #include "scsi_priv.h" #include "scsi_logging.h" +static void scsi_done(struct scsi_cmnd *cmd); +static int scsi_retry_command(struct scsi_cmnd *cmd); /* * Definitions and constants. @@ -741,7 +743,7 @@ static DEFINE_PER_CPU(struct list_head, scsi_done_q); * * This function is interrupt context safe. */ -void scsi_done(struct scsi_cmnd *cmd) +static void scsi_done(struct scsi_cmnd *cmd) { /* * We don't have to worry about this one timing out any more. @@ -836,7 +838,7 @@ static void scsi_softirq(struct softirq_action *h) * level drivers should not become re-entrant as a result of * this. */ -int scsi_retry_command(struct scsi_cmnd *cmd) +static int scsi_retry_command(struct scsi_cmnd *cmd) { /* * Restore the SCSI command state. diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index e0208886b45e..322b5a41a36f 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1783,7 +1783,7 @@ static void __exit scsi_debug_exit(void) device_initcall(scsi_debug_init); module_exit(scsi_debug_exit); -void pseudo_0_release(struct device * dev) +static void pseudo_0_release(struct device * dev) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n"); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9f996499fa9d..621dee8b8cb2 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -44,7 +44,7 @@ struct scsi_host_sg_pool { #endif #define SP(x) { x, "sgpool-" #x } -struct scsi_host_sg_pool scsi_sg_pools[] = { +static struct scsi_host_sg_pool scsi_sg_pools[] = { SP(8), SP(16), SP(32), diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index c01580df4476..96d4f745975c 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -61,8 +61,6 @@ extern void scsi_exit_hosts(void); extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd); extern int scsi_setup_command_freelist(struct Scsi_Host *shost); extern void scsi_destroy_command_freelist(struct Scsi_Host *shost); -extern void scsi_done(struct scsi_cmnd *cmd); -extern int scsi_retry_command(struct scsi_cmnd *cmd); extern int scsi_insert_special_req(struct scsi_request *sreq, int); extern void scsi_init_cmd_from_req(struct scsi_cmnd *cmd, struct scsi_request *sreq); @@ -136,7 +134,6 @@ extern void scsi_exit_sysctl(void); #endif /* CONFIG_SYSCTL */ /* scsi_sysfs.c */ -extern void scsi_device_dev_release(struct device *); extern int scsi_sysfs_add_sdev(struct scsi_device *); extern int scsi_sysfs_add_host(struct Scsi_Host *); extern int scsi_sysfs_register(void); @@ -145,7 +142,6 @@ extern void scsi_sysfs_device_initialize(struct scsi_device *); extern int scsi_sysfs_target_initialize(struct scsi_device *); extern struct scsi_transport_template blank_transport_template; -extern struct class sdev_class; extern struct bus_type scsi_bus_type; /* diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 93b41100a6d8..beed7fbe1cbe 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -150,7 +150,7 @@ static void scsi_device_cls_release(struct class_device *class_dev) put_device(&sdev->sdev_gendev); } -void scsi_device_dev_release(struct device *dev) +static void scsi_device_dev_release(struct device *dev) { struct scsi_device *sdev; struct device *parent; @@ -185,7 +185,7 @@ void scsi_device_dev_release(struct device *dev) put_device(parent); } -struct class sdev_class = { +static struct class sdev_class = { .name = "scsi_device", .release = scsi_device_cls_release, }; diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 30e8beb71430..d8b9d2b8c200 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -132,9 +132,9 @@ struct uart_8250_port { struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ - unsigned int capabilities; /* port capabilities */ + unsigned short capabilities; /* port capabilities */ + unsigned short bugs; /* port bugs */ unsigned int tx_loadsz; /* transmit fifo load size */ - unsigned short rev; unsigned char acr; unsigned char ier; unsigned char lcr; @@ -560,7 +560,14 @@ static void autoconfig_has_efr(struct uart_8250_port *up) if (id1 == 0x16 && id2 == 0xC9 && (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { up->port.type = PORT_16C950; - up->rev = rev | (id3 << 8); + + /* + * Enable work around for the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if (id3 == 0x52 && rev == 0x01) + up->bugs |= UART_BUG_QUOT; return; } @@ -577,8 +584,6 @@ static void autoconfig_has_efr(struct uart_8250_port *up) id2 = id1 >> 8; if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) { - if (id2 == 0x10) - up->rev = id1 & 255; up->port.type = PORT_16850; return; } @@ -809,6 +814,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) // save_flags(flags); cli(); up->capabilities = 0; + up->bugs = 0; if (!(up->port.flags & UPF_BUGGY_UART)) { /* @@ -1021,6 +1027,8 @@ static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop) } } +static void transmit_chars(struct uart_8250_port *up); + static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start) { struct uart_8250_port *up = (struct uart_8250_port *)port; @@ -1028,6 +1036,14 @@ static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start) if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier); + + if (up->bugs & UART_BUG_TXEN) { + unsigned char lsr, iir; + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) + transmit_chars(up); + } } /* * We only do this from uart_start @@ -1433,6 +1449,7 @@ static int serial8250_startup(struct uart_port *port) { struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; + unsigned char lsr, iir; int retval; up->capabilities = uart_config[up->port.type].flags; @@ -1536,6 +1553,26 @@ static int serial8250_startup(struct uart_port *port) up->port.mctrl |= TIOCM_OUT2; serial8250_set_mctrl(&up->port, up->port.mctrl); + + /* + * Do a quick test to see if we receive an + * interrupt when we enable the TX irq. + */ + serial_outp(up, UART_IER, UART_IER_THRI); + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + serial_outp(up, UART_IER, 0); + + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (!(up->bugs & UART_BUG_TXEN)) { + up->bugs |= UART_BUG_TXEN; + pr_debug("ttyS%d - enabling bad tx status workarounds\n", + port->line); + } + } else { + up->bugs &= ~UART_BUG_TXEN; + } + spin_unlock_irqrestore(&up->port.lock, flags); /* @@ -1645,22 +1682,22 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, switch (termios->c_cflag & CSIZE) { case CS5: - cval = 0x00; + cval = UART_LCR_WLEN5; break; case CS6: - cval = 0x01; + cval = UART_LCR_WLEN6; break; case CS7: - cval = 0x02; + cval = UART_LCR_WLEN7; break; default: case CS8: - cval = 0x03; + cval = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) - cval |= 0x04; + cval |= UART_LCR_STOP; if (termios->c_cflag & PARENB) cval |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) @@ -1677,12 +1714,9 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, quot = serial8250_get_divisor(port, baud); /* - * Work around a bug in the Oxford Semiconductor 952 rev B - * chip which causes it to seriously miscalculate baud rates - * when DLL is 0. + * Oxford Semi 952 rev B workaround */ - if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && - up->rev == 0x5201) + if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) quot ++; if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index 4f3d62f222f4..9225c82faeb8 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -51,6 +51,9 @@ struct serial8250_config { #define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ +#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ +#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ + #if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) #define _INLINE_ inline #else diff --git a/drivers/serial/au1x00_uart.c b/drivers/serial/au1x00_uart.c index b6d3d5034940..5400dc2c087e 100644 --- a/drivers/serial/au1x00_uart.c +++ b/drivers/serial/au1x00_uart.c @@ -773,22 +773,22 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, switch (termios->c_cflag & CSIZE) { case CS5: - cval = 0x00; + cval = UART_LCR_WLEN5; break; case CS6: - cval = 0x01; + cval = UART_LCR_WLEN6; break; case CS7: - cval = 0x02; + cval = UART_LCR_WLEN7; break; default: case CS8: - cval = 0x03; + cval = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) - cval |= 0x04; + cval |= UART_LCR_STOP; if (termios->c_cflag & PARENB) cval |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) diff --git a/drivers/serial/m32r_sio.c b/drivers/serial/m32r_sio.c index 08d61f13edc6..0301feacbde4 100644 --- a/drivers/serial/m32r_sio.c +++ b/drivers/serial/m32r_sio.c @@ -724,22 +724,22 @@ static void m32r_sio_set_termios(struct uart_port *port, switch (termios->c_cflag & CSIZE) { case CS5: - cval = 0x00; + cval = UART_LCR_WLEN5; break; case CS6: - cval = 0x01; + cval = UART_LCR_WLEN6; break; case CS7: - cval = 0x02; + cval = UART_LCR_WLEN7; break; default: case CS8: - cval = 0x03; + cval = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) - cval |= 0x04; + cval |= UART_LCR_STOP; if (termios->c_cflag & PARENB) cval |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index 9dc151d8fa61..08b08d6ae904 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -455,22 +455,22 @@ serial_pxa_set_termios(struct uart_port *port, struct termios *termios, switch (termios->c_cflag & CSIZE) { case CS5: - cval = 0x00; + cval = UART_LCR_WLEN5; break; case CS6: - cval = 0x01; + cval = UART_LCR_WLEN6; break; case CS7: - cval = 0x02; + cval = UART_LCR_WLEN7; break; default: case CS8: - cval = 0x03; + cval = UART_LCR_WLEN8; break; } if (termios->c_cflag & CSTOPB) - cval |= 0x04; + cval |= UART_LCR_STOP; if (termios->c_cflag & PARENB) cval |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index 2a9f7ade2c9d..5c4678478b1d 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -198,7 +198,7 @@ static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port) /* translate a port to the device name */ -static inline char *s3c24xx_serial_portname(struct uart_port *port) +static inline const char *s3c24xx_serial_portname(struct uart_port *port) { return to_platform_device(port->dev)->name; } @@ -903,7 +903,7 @@ static void s3c24xx_serial_release_port(struct uart_port *port) static int s3c24xx_serial_request_port(struct uart_port *port) { - char *name = s3c24xx_serial_portname(port); + const char *name = s3c24xx_serial_portname(port); return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; } diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c index fee6418e84c4..840815fde49b 100644 --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -572,6 +572,7 @@ static void sn_transmit_chars(struct sn_cons_port *port, int raw) if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) { /* Nothing to do. */ + ia64_sn_console_intr_disable(SAL_CONSOLE_INTR_XMIT); return; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d041782e0c8b..0da23732e807 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1794,7 +1794,7 @@ EXPORT_SYMBOL (usb_remove_hcd); /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) +#if defined(CONFIG_USB_MON) struct usb_mon_operations *mon_ops; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index f67cf1e634fc..325a51656c3f 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -399,7 +399,7 @@ static inline void usbfs_cleanup(void) { } /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) +#if defined(CONFIG_USB_MON) struct usb_mon_operations { void (*urb_submit)(struct usb_bus *bus, struct urb *urb); diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index f9f9561c6bad..c3e3a95d3804 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -453,17 +453,6 @@ static int usbfs_fill_super(struct super_block *sb, void *data, int silent) return 0; } -static struct dentry * get_dentry(struct dentry *parent, const char *name) -{ - struct qstr qstr; - - qstr.name = name; - qstr.len = strlen(name); - qstr.hash = full_name_hash(name,qstr.len); - return lookup_hash(&qstr,parent); -} - - /* * fs_create_by_name - create a file, given a name * @name: name of file @@ -496,7 +485,7 @@ static int fs_create_by_name (const char *name, mode_t mode, *dentry = NULL; down(&parent->d_inode->i_sem); - *dentry = get_dentry (parent, name); + *dentry = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(dentry)) { if ((mode & S_IFMT) == S_IFDIR) error = usbfs_mkdir (parent->d_inode, *dentry, mode); diff --git a/drivers/usb/media/pwc/pwc-uncompress.c b/drivers/usb/media/pwc/pwc-uncompress.c index bc3b1635eab0..ef4204eab6c4 100644 --- a/drivers/usb/media/pwc/pwc-uncompress.c +++ b/drivers/usb/media/pwc/pwc-uncompress.c @@ -118,9 +118,9 @@ int pwc_decompress(struct pwc_device *pdev) return -ENXIO; /* No such device or address: missing decompressor */ } +#if 0 switch (pdev->type) { -#if 0 case 675: case 680: case 690: @@ -128,18 +128,17 @@ int pwc_decompress(struct pwc_device *pdev) case 730: case 740: case 750: - pwc_dec23_decompress(&pdev->image, &pdev->view, &pdev->offset, - yuv, image, - flags, + pwc_dec23_decompress(&pdev->image, &pdev->view, + &pdev->offset, yuv, image, flags, pdev->decompress_data, pdev->vbandlength); break; case 645: case 646: /* TODO & FIXME */ -#endif - return -ENXIO; /* No such device or address: missing decompressor */ + return -ENXIO; /* Missing decompressor */ break; } +#endif } return 0; } diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig index 4e6152aa5f19..777642e26b9a 100644 --- a/drivers/usb/mon/Kconfig +++ b/drivers/usb/mon/Kconfig @@ -2,13 +2,9 @@ # USB Monitor configuration # -# In normal life, it makes little sense to have usbmon as a module, and in fact -# it is harmful, because there is no way to autoload the module. -# The 'm' option is allowed for hackers who debug the usbmon itself, -# and for those who have usbcore as a module. config USB_MON - tristate "USB Monitor" - depends on USB + bool "USB Monitor" + depends on USB!=n default y help If you say Y here, a component which captures the USB traffic @@ -17,6 +13,5 @@ config USB_MON Harding's USBMon. This is somewhat experimental at this time, but it should be safe, - as long as you aren't building this as a module and then removing it. - - If unsure, say Y. Do not say M. + as long as you aren't using modular USB and try to remove this + module. diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile index 3cff8d444bb1..f18d10ce91f9 100644 --- a/drivers/usb/mon/Makefile +++ b/drivers/usb/mon/Makefile @@ -4,4 +4,4 @@ usbmon-objs := mon_main.o mon_stat.o mon_text.o -obj-$(CONFIG_USB_MON) += usbmon.o +obj-$(CONFIG_USB) += usbmon.o diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 815fbc8317fc..16e37a535d85 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -460,7 +461,7 @@ static inline unsigned int get_pcd(unsigned int pixclock) * speeds */ pcd = (unsigned long long)get_lcdclk_frequency_10khz() * pixclock; - pcd /= 100000000 * 2; + do_div(pcd, 100000000 * 2); /* no need for this, since we should subtract 1 anyway. they cancel */ /* pcd += 1; */ /* make up for integer math truncations */ return (unsigned int)pcd; diff --git a/fs/Kconfig b/fs/Kconfig index a7c0cc3203cb..8157f2e2d515 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -50,6 +50,23 @@ config EXT2_FS_SECURITY If you are not using a security module that requires using extended attributes for file security labels, say N. +config EXT2_FS_XIP + bool "Ext2 execute in place support" + depends on EXT2_FS + help + Execute in place can be used on memory-backed block devices. If you + enable this option, you can select to mount block devices which are + capable of this feature without using the page cache. + + If you do not use a block device that is capable of using this, + or if unsure, say N. + +config FS_XIP +# execute in place + bool + depends on EXT2_FS_XIP + default y + config EXT3_FS tristate "Ext3 journalling file system support" help @@ -1413,6 +1430,8 @@ config NFSD_V4 bool "Provide NFSv4 server support (EXPERIMENTAL)" depends on NFSD_V3 && EXPERIMENTAL select NFSD_TCP + select CRYPTO_MD5 + select CRYPTO help If you would like to include the NFSv4 server as well as the NFSv2 and NFSv3 servers, say Y here. This feature is experimental, and diff --git a/fs/block_dev.c b/fs/block_dev.c index c0cbd1bc1a02..e0df94c37b7e 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -777,8 +777,7 @@ static ssize_t blkdev_file_aio_write(struct kiocb *iocb, const char __user *buf, return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); } -static int block_ioctl(struct inode *inode, struct file *file, unsigned cmd, - unsigned long arg) +static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) { return blkdev_ioctl(file->f_mapping->host, file, cmd, arg); } @@ -803,7 +802,7 @@ struct file_operations def_blk_fops = { .aio_write = blkdev_file_aio_write, .mmap = generic_file_mmap, .fsync = block_fsync, - .ioctl = block_ioctl, + .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif diff --git a/fs/buffer.c b/fs/buffer.c index 0befa724ab98..13e5938a64f6 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -331,7 +331,7 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync) return ret; } -asmlinkage long sys_fsync(unsigned int fd) +static long do_fsync(unsigned int fd, int datasync) { struct file * file; struct address_space *mapping; @@ -342,14 +342,14 @@ asmlinkage long sys_fsync(unsigned int fd) if (!file) goto out; - mapping = file->f_mapping; - ret = -EINVAL; if (!file->f_op || !file->f_op->fsync) { /* Why? We can still call filemap_fdatawrite */ goto out_putf; } + mapping = file->f_mapping; + current->flags |= PF_SYNCWRITE; ret = filemap_fdatawrite(mapping); @@ -358,7 +358,7 @@ asmlinkage long sys_fsync(unsigned int fd) * which could cause livelocks in fsync_buffers_list */ down(&mapping->host->i_sem); - err = file->f_op->fsync(file, file->f_dentry, 0); + err = file->f_op->fsync(file, file->f_dentry, datasync); if (!ret) ret = err; up(&mapping->host->i_sem); @@ -373,39 +373,14 @@ out: return ret; } +asmlinkage long sys_fsync(unsigned int fd) +{ + return do_fsync(fd, 0); +} + asmlinkage long sys_fdatasync(unsigned int fd) { - struct file * file; - struct address_space *mapping; - int ret, err; - - ret = -EBADF; - file = fget(fd); - if (!file) - goto out; - - ret = -EINVAL; - if (!file->f_op || !file->f_op->fsync) - goto out_putf; - - mapping = file->f_mapping; - - current->flags |= PF_SYNCWRITE; - ret = filemap_fdatawrite(mapping); - down(&mapping->host->i_sem); - err = file->f_op->fsync(file, file->f_dentry, 1); - if (!ret) - ret = err; - up(&mapping->host->i_sem); - err = filemap_fdatawait(mapping); - if (!ret) - ret = err; - current->flags &= ~PF_SYNCWRITE; - -out_putf: - fput(file); -out: - return ret; + return do_fsync(fd, 1); } /* @@ -1951,7 +1926,6 @@ static int __block_prepare_write(struct inode *inode, struct page *page, if (err) break; if (buffer_new(bh)) { - clear_buffer_new(bh); unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); if (PageUptodate(page)) { @@ -1993,9 +1967,14 @@ static int __block_prepare_write(struct inode *inode, struct page *page, if (!buffer_uptodate(*wait_bh)) err = -EIO; } - if (!err) - return err; - + if (!err) { + bh = head; + do { + if (buffer_new(bh)) + clear_buffer_new(bh); + } while ((bh = bh->b_this_page) != head); + return 0; + } /* Error case: */ /* * Zero out any newly allocated blocks to avoid exposing stale diff --git a/fs/char_dev.c b/fs/char_dev.c index c1e3537909fc..e82aac9cc2f5 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -56,10 +56,21 @@ int get_chrdev_list(char *page) down(&chrdevs_lock); for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { - for (cd = chrdevs[i]; cd; cd = cd->next) + for (cd = chrdevs[i]; cd; cd = cd->next) { + /* + * if the current name, plus the 5 extra characters + * in the device line for this entry + * would run us off the page, we're done + */ + if ((len+strlen(cd->name) + 5) >= PAGE_SIZE) + goto page_full; + + len += sprintf(page+len, "%3d %s\n", cd->major, cd->name); + } } +page_full: up(&chrdevs_lock); return len; diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index b529786699e7..a86ac4aeaedb 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -110,16 +110,6 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent) return simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); } -static struct dentry * get_dentry(struct dentry *parent, const char *name) -{ - struct qstr qstr; - - qstr.name = name; - qstr.len = strlen(name); - qstr.hash = full_name_hash(name,qstr.len); - return lookup_hash(&qstr,parent); -} - static struct super_block *debug_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) @@ -157,7 +147,7 @@ static int debugfs_create_by_name(const char *name, mode_t mode, *dentry = NULL; down(&parent->d_inode->i_sem); - *dentry = get_dentry (parent, name); + *dentry = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(dentry)) { if ((mode & S_IFMT) == S_IFDIR) error = debugfs_mkdir(parent->d_inode, *dentry, mode); diff --git a/fs/direct-io.c b/fs/direct-io.c index 1d55e7e67342..0d06097bc995 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -215,7 +215,7 @@ static struct page *dio_get_page(struct dio *dio) static void dio_complete(struct dio *dio, loff_t offset, ssize_t bytes) { if (dio->end_io && dio->result) - dio->end_io(dio->inode, offset, bytes, dio->map_bh.b_private); + dio->end_io(dio->iocb, offset, bytes, dio->map_bh.b_private); if (dio->lock_type == DIO_LOCKING) up_read(&dio->inode->i_alloc_sem); } diff --git a/fs/dquot.c b/fs/dquot.c index 3995ce7907cc..37212b039a4a 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1519,14 +1519,22 @@ out_path: * This function is used when filesystem needs to initialize quotas * during mount time. */ -int vfs_quota_on_mount(int type, int format_id, struct dentry *dentry) +int vfs_quota_on_mount(struct super_block *sb, char *qf_name, + int format_id, int type) { + struct dentry *dentry; int error; + dentry = lookup_one_len(qf_name, sb->s_root, strlen(qf_name)); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + error = security_quota_on(dentry); - if (error) - return error; - return vfs_quota_on_inode(dentry->d_inode, type, format_id); + if (!error) + error = vfs_quota_on_inode(dentry->d_inode, type, format_id); + + dput(dentry); + return error; } /* Generic routine for getting common part of quota structure */ diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 9900e333655a..6ab1dd0ca904 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -101,57 +101,6 @@ /* Maximum number of poll wake up nests we are allowing */ #define EP_MAX_POLLWAKE_NESTS 4 -/* Macro to allocate a "struct epitem" from the slab cache */ -#define EPI_MEM_ALLOC() (struct epitem *) kmem_cache_alloc(epi_cache, SLAB_KERNEL) - -/* Macro to free a "struct epitem" to the slab cache */ -#define EPI_MEM_FREE(p) kmem_cache_free(epi_cache, p) - -/* Macro to allocate a "struct eppoll_entry" from the slab cache */ -#define PWQ_MEM_ALLOC() (struct eppoll_entry *) kmem_cache_alloc(pwq_cache, SLAB_KERNEL) - -/* Macro to free a "struct eppoll_entry" to the slab cache */ -#define PWQ_MEM_FREE(p) kmem_cache_free(pwq_cache, p) - -/* Fast test to see if the file is an evenpoll file */ -#define IS_FILE_EPOLL(f) ((f)->f_op == &eventpoll_fops) - -/* Setup the structure that is used as key for the rb-tree */ -#define EP_SET_FFD(p, f, d) do { (p)->file = (f); (p)->fd = (d); } while (0) - -/* Compare rb-tree keys */ -#define EP_CMP_FFD(p1, p2) ((p1)->file > (p2)->file ? +1: \ - ((p1)->file < (p2)->file ? -1: (p1)->fd - (p2)->fd)) - -/* Special initialization for the rb-tree node to detect linkage */ -#define EP_RB_INITNODE(n) (n)->rb_parent = (n) - -/* Removes a node from the rb-tree and marks it for a fast is-linked check */ -#define EP_RB_ERASE(n, r) do { rb_erase(n, r); (n)->rb_parent = (n); } while (0) - -/* Fast check to verify that the item is linked to the main rb-tree */ -#define EP_RB_LINKED(n) ((n)->rb_parent != (n)) - -/* - * Remove the item from the list and perform its initialization. - * This is useful for us because we can test if the item is linked - * using "EP_IS_LINKED(p)". - */ -#define EP_LIST_DEL(p) do { list_del(p); INIT_LIST_HEAD(p); } while (0) - -/* Tells us if the item is currently linked */ -#define EP_IS_LINKED(p) (!list_empty(p)) - -/* Get the "struct epitem" from a wait queue pointer */ -#define EP_ITEM_FROM_WAIT(p) ((struct epitem *) container_of(p, struct eppoll_entry, wait)->base) - -/* Get the "struct epitem" from an epoll queue wrapper */ -#define EP_ITEM_FROM_EPQUEUE(p) (container_of(p, struct ep_pqueue, pt)->epi) - -/* Tells if the epoll_ctl(2) operation needs an event copy from userspace */ -#define EP_OP_HASH_EVENT(op) ((op) != EPOLL_CTL_DEL) - - struct epoll_filefd { struct file *file; int fd; @@ -357,6 +306,82 @@ static struct dentry_operations eventpollfs_dentry_operations = { +/* Fast test to see if the file is an evenpoll file */ +static inline int is_file_epoll(struct file *f) +{ + return f->f_op == &eventpoll_fops; +} + +/* Setup the structure that is used as key for the rb-tree */ +static inline void ep_set_ffd(struct epoll_filefd *ffd, + struct file *file, int fd) +{ + ffd->file = file; + ffd->fd = fd; +} + +/* Compare rb-tree keys */ +static inline int ep_cmp_ffd(struct epoll_filefd *p1, + struct epoll_filefd *p2) +{ + return (p1->file > p2->file ? +1: + (p1->file < p2->file ? -1 : p1->fd - p2->fd)); +} + +/* Special initialization for the rb-tree node to detect linkage */ +static inline void ep_rb_initnode(struct rb_node *n) +{ + n->rb_parent = n; +} + +/* Removes a node from the rb-tree and marks it for a fast is-linked check */ +static inline void ep_rb_erase(struct rb_node *n, struct rb_root *r) +{ + rb_erase(n, r); + n->rb_parent = n; +} + +/* Fast check to verify that the item is linked to the main rb-tree */ +static inline int ep_rb_linked(struct rb_node *n) +{ + return n->rb_parent != n; +} + +/* + * Remove the item from the list and perform its initialization. + * This is useful for us because we can test if the item is linked + * using "ep_is_linked(p)". + */ +static inline void ep_list_del(struct list_head *p) +{ + list_del(p); + INIT_LIST_HEAD(p); +} + +/* Tells us if the item is currently linked */ +static inline int ep_is_linked(struct list_head *p) +{ + return !list_empty(p); +} + +/* Get the "struct epitem" from a wait queue pointer */ +static inline struct epitem * ep_item_from_wait(wait_queue_t *p) +{ + return container_of(p, struct eppoll_entry, wait)->base; +} + +/* Get the "struct epitem" from an epoll queue wrapper */ +static inline struct epitem * ep_item_from_epqueue(poll_table *p) +{ + return container_of(p, struct ep_pqueue, pt)->epi; +} + +/* Tells if the epoll_ctl(2) operation needs an event copy from userspace */ +static inline int ep_op_hash_event(int op) +{ + return op != EPOLL_CTL_DEL; +} + /* Initialize the poll safe wake up structure */ static void ep_poll_safewake_init(struct poll_safewake *psw) { @@ -456,7 +481,7 @@ void eventpoll_release_file(struct file *file) epi = list_entry(lsthead->next, struct epitem, fllink); ep = epi->ep; - EP_LIST_DEL(&epi->fllink); + ep_list_del(&epi->fllink); down_write(&ep->sem); ep_remove(ep, epi); up_write(&ep->sem); @@ -534,7 +559,7 @@ sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event) current, epfd, op, fd, event)); error = -EFAULT; - if (EP_OP_HASH_EVENT(op) && + if (ep_op_hash_event(op) && copy_from_user(&epds, event, sizeof(struct epoll_event))) goto eexit_1; @@ -560,7 +585,7 @@ sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event) * adding an epoll file descriptor inside itself. */ error = -EINVAL; - if (file == tfile || !IS_FILE_EPOLL(file)) + if (file == tfile || !is_file_epoll(file)) goto eexit_3; /* @@ -656,7 +681,7 @@ asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, * the user passed to us _is_ an eventpoll file. */ error = -EINVAL; - if (!IS_FILE_EPOLL(file)) + if (!is_file_epoll(file)) goto eexit_2; /* @@ -831,11 +856,11 @@ static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd) struct epitem *epi, *epir = NULL; struct epoll_filefd ffd; - EP_SET_FFD(&ffd, file, fd); + ep_set_ffd(&ffd, file, fd); read_lock_irqsave(&ep->lock, flags); for (rbp = ep->rbr.rb_node; rbp; ) { epi = rb_entry(rbp, struct epitem, rbn); - kcmp = EP_CMP_FFD(&ffd, &epi->ffd); + kcmp = ep_cmp_ffd(&ffd, &epi->ffd); if (kcmp > 0) rbp = rbp->rb_right; else if (kcmp < 0) @@ -875,7 +900,7 @@ static void ep_release_epitem(struct epitem *epi) { if (atomic_dec_and_test(&epi->usecnt)) - EPI_MEM_FREE(epi); + kmem_cache_free(epi_cache, epi); } @@ -886,10 +911,10 @@ static void ep_release_epitem(struct epitem *epi) static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead, poll_table *pt) { - struct epitem *epi = EP_ITEM_FROM_EPQUEUE(pt); + struct epitem *epi = ep_item_from_epqueue(pt); struct eppoll_entry *pwq; - if (epi->nwait >= 0 && (pwq = PWQ_MEM_ALLOC())) { + if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, SLAB_KERNEL))) { init_waitqueue_func_entry(&pwq->wait, ep_poll_callback); pwq->whead = whead; pwq->base = epi; @@ -912,7 +937,7 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi) while (*p) { parent = *p; epic = rb_entry(parent, struct epitem, rbn); - kcmp = EP_CMP_FFD(&epi->ffd, &epic->ffd); + kcmp = ep_cmp_ffd(&epi->ffd, &epic->ffd); if (kcmp > 0) p = &parent->rb_right; else @@ -932,17 +957,17 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, struct ep_pqueue epq; error = -ENOMEM; - if (!(epi = EPI_MEM_ALLOC())) + if (!(epi = kmem_cache_alloc(epi_cache, SLAB_KERNEL))) goto eexit_1; /* Item initialization follow here ... */ - EP_RB_INITNODE(&epi->rbn); + ep_rb_initnode(&epi->rbn); INIT_LIST_HEAD(&epi->rdllink); INIT_LIST_HEAD(&epi->fllink); INIT_LIST_HEAD(&epi->txlink); INIT_LIST_HEAD(&epi->pwqlist); epi->ep = ep; - EP_SET_FFD(&epi->ffd, tfile, fd); + ep_set_ffd(&epi->ffd, tfile, fd); epi->event = *event; atomic_set(&epi->usecnt, 1); epi->nwait = 0; @@ -978,7 +1003,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, ep_rbtree_insert(ep, epi); /* If the file is already "ready" we drop it inside the ready list */ - if ((revents & event->events) && !EP_IS_LINKED(&epi->rdllink)) { + if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); /* Notify waiting tasks that events are available */ @@ -1007,11 +1032,11 @@ eexit_2: * allocated wait queue. */ write_lock_irqsave(&ep->lock, flags); - if (EP_IS_LINKED(&epi->rdllink)) - EP_LIST_DEL(&epi->rdllink); + if (ep_is_linked(&epi->rdllink)) + ep_list_del(&epi->rdllink); write_unlock_irqrestore(&ep->lock, flags); - EPI_MEM_FREE(epi); + kmem_cache_free(epi_cache, epi); eexit_1: return error; } @@ -1050,14 +1075,14 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even * If the item is not linked to the hash it means that it's on its * way toward the removal. Do nothing in this case. */ - if (EP_RB_LINKED(&epi->rbn)) { + if (ep_rb_linked(&epi->rbn)) { /* * If the item is "hot" and it is not registered inside the ready * list, push it inside. If the item is not "hot" and it is currently * registered inside the ready list, unlink it. */ if (revents & event->events) { - if (!EP_IS_LINKED(&epi->rdllink)) { + if (!ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); /* Notify waiting tasks that events are available */ @@ -1097,9 +1122,9 @@ static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi) while (!list_empty(lsthead)) { pwq = list_entry(lsthead->next, struct eppoll_entry, llink); - EP_LIST_DEL(&pwq->llink); + ep_list_del(&pwq->llink); remove_wait_queue(pwq->whead, &pwq->wait); - PWQ_MEM_FREE(pwq); + kmem_cache_free(pwq_cache, pwq); } } } @@ -1118,7 +1143,7 @@ static int ep_unlink(struct eventpoll *ep, struct epitem *epi) * The check protect us from doing a double unlink ( crash ). */ error = -ENOENT; - if (!EP_RB_LINKED(&epi->rbn)) + if (!ep_rb_linked(&epi->rbn)) goto eexit_1; /* @@ -1133,14 +1158,14 @@ static int ep_unlink(struct eventpoll *ep, struct epitem *epi) * This operation togheter with the above check closes the door to * double unlinks. */ - EP_RB_ERASE(&epi->rbn, &ep->rbr); + ep_rb_erase(&epi->rbn, &ep->rbr); /* * If the item we are going to remove is inside the ready file descriptors * we want to remove it from this list to avoid stale events. */ - if (EP_IS_LINKED(&epi->rdllink)) - EP_LIST_DEL(&epi->rdllink); + if (ep_is_linked(&epi->rdllink)) + ep_list_del(&epi->rdllink); error = 0; eexit_1: @@ -1174,8 +1199,8 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi) /* Remove the current item from the list of epoll hooks */ spin_lock(&file->f_ep_lock); - if (EP_IS_LINKED(&epi->fllink)) - EP_LIST_DEL(&epi->fllink); + if (ep_is_linked(&epi->fllink)) + ep_list_del(&epi->fllink); spin_unlock(&file->f_ep_lock); /* We need to acquire the write IRQ lock before calling ep_unlink() */ @@ -1210,7 +1235,7 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k { int pwake = 0; unsigned long flags; - struct epitem *epi = EP_ITEM_FROM_WAIT(wait); + struct epitem *epi = ep_item_from_wait(wait); struct eventpoll *ep = epi->ep; DNPRINTK(3, (KERN_INFO "[%p] eventpoll: poll_callback(%p) epi=%p ep=%p\n", @@ -1228,7 +1253,7 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k goto is_disabled; /* If this file is already in the ready list we exit soon */ - if (EP_IS_LINKED(&epi->rdllink)) + if (ep_is_linked(&epi->rdllink)) goto is_linked; list_add_tail(&epi->rdllink, &ep->rdllist); @@ -1307,7 +1332,7 @@ static int ep_collect_ready_items(struct eventpoll *ep, struct list_head *txlist lnk = lnk->next; /* If this file is already in the ready list we exit soon */ - if (!EP_IS_LINKED(&epi->txlink)) { + if (!ep_is_linked(&epi->txlink)) { /* * This is initialized in this way so that the default * behaviour of the reinjecting code will be to push back @@ -1322,7 +1347,7 @@ static int ep_collect_ready_items(struct eventpoll *ep, struct list_head *txlist /* * Unlink the item from the ready list. */ - EP_LIST_DEL(&epi->rdllink); + ep_list_del(&epi->rdllink); } } @@ -1401,7 +1426,7 @@ static void ep_reinject_items(struct eventpoll *ep, struct list_head *txlist) epi = list_entry(txlist->next, struct epitem, txlink); /* Unlink the current item from the transfer list */ - EP_LIST_DEL(&epi->txlink); + ep_list_del(&epi->txlink); /* * If the item is no more linked to the interest set, we don't @@ -1410,8 +1435,8 @@ static void ep_reinject_items(struct eventpoll *ep, struct list_head *txlist) * item is set to have an Edge Triggered behaviour, we don't have * to push it back either. */ - if (EP_RB_LINKED(&epi->rbn) && !(epi->event.events & EPOLLET) && - (epi->revents & epi->event.events) && !EP_IS_LINKED(&epi->rdllink)) { + if (ep_rb_linked(&epi->rbn) && !(epi->event.events & EPOLLET) && + (epi->revents & epi->event.events) && !ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); ricnt++; } diff --git a/fs/exec.c b/fs/exec.c index 3a4b35a14c0d..48871917d363 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -58,6 +58,9 @@ int core_uses_pid; char core_pattern[65] = "core"; +int suid_dumpable = 0; + +EXPORT_SYMBOL(suid_dumpable); /* The maximal length of core_pattern is also specified in sysctl.c */ static struct linux_binfmt *formats; @@ -864,6 +867,9 @@ int flush_old_exec(struct linux_binprm * bprm) if (current->euid == current->uid && current->egid == current->gid) current->mm->dumpable = 1; + else + current->mm->dumpable = suid_dumpable; + name = bprm->filename; /* Copies the binary name from after last slash */ @@ -884,7 +890,7 @@ int flush_old_exec(struct linux_binprm * bprm) permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) || (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { suid_keys(current); - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; } /* An exec changes our domain. We are no longer part of the thread @@ -1432,6 +1438,8 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) struct inode * inode; struct file * file; int retval = 0; + int fsuid = current->fsuid; + int flag = 0; binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) @@ -1441,6 +1449,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) up_write(&mm->mmap_sem); goto fail; } + + /* + * We cannot trust fsuid as being the "true" uid of the + * process nor do we know its entire history. We only know it + * was tainted so we dump it as root in mode 2. + */ + if (mm->dumpable == 2) { /* Setuid core dump mode */ + flag = O_EXCL; /* Stop rewrite attacks */ + current->fsuid = 0; /* Dump root private */ + } mm->dumpable = 0; init_completion(&mm->core_done); spin_lock_irq(¤t->sighand->siglock); @@ -1466,7 +1484,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) lock_kernel(); format_corename(corename, core_pattern, signr); unlock_kernel(); - file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600); + file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 0600); if (IS_ERR(file)) goto fail_unlock; inode = file->f_dentry->d_inode; @@ -1491,6 +1509,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) close_fail: filp_close(file, NULL); fail_unlock: + current->fsuid = fsuid; complete_all(&mm->core_done); fail: return retval; diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index ee240a14e70f..c5d02da73bc3 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -10,3 +10,4 @@ ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o +ext2-$(CONFIG_EXT2_FS_XIP) += xip.o diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 25f4a64fd6bc..213148c36ebe 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -396,12 +396,12 @@ static size_t ext2_xattr_list_acl_access(struct inode *inode, char *list, size_t list_size, const char *name, size_t name_len) { - const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); + const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS); if (!test_opt(inode->i_sb, POSIX_ACL)) return 0; if (list && size <= list_size) - memcpy(list, XATTR_NAME_ACL_ACCESS, size); + memcpy(list, POSIX_ACL_XATTR_ACCESS, size); return size; } @@ -409,12 +409,12 @@ static size_t ext2_xattr_list_acl_default(struct inode *inode, char *list, size_t list_size, const char *name, size_t name_len) { - const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); + const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT); if (!test_opt(inode->i_sb, POSIX_ACL)) return 0; if (list && size <= list_size) - memcpy(list, XATTR_NAME_ACL_DEFAULT, size); + memcpy(list, POSIX_ACL_XATTR_DEFAULT, size); return size; } @@ -506,14 +506,14 @@ ext2_xattr_set_acl_default(struct inode *inode, const char *name, } struct xattr_handler ext2_xattr_acl_access_handler = { - .prefix = XATTR_NAME_ACL_ACCESS, + .prefix = POSIX_ACL_XATTR_ACCESS, .list = ext2_xattr_list_acl_access, .get = ext2_xattr_get_acl_access, .set = ext2_xattr_set_acl_access, }; struct xattr_handler ext2_xattr_acl_default_handler = { - .prefix = XATTR_NAME_ACL_DEFAULT, + .prefix = POSIX_ACL_XATTR_DEFAULT, .list = ext2_xattr_list_acl_default, .get = ext2_xattr_get_acl_default, .set = ext2_xattr_set_acl_default, diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h index fed96ae81a7d..0bde85bafe38 100644 --- a/fs/ext2/acl.h +++ b/fs/ext2/acl.h @@ -4,7 +4,7 @@ (C) 2001 Andreas Gruenbacher, */ -#include +#include #define EXT2_ACL_VERSION 0x0001 diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 8f0fd726c3f1..eed521d22cf0 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -147,9 +147,11 @@ extern struct file_operations ext2_dir_operations; /* file.c */ extern struct inode_operations ext2_file_inode_operations; extern struct file_operations ext2_file_operations; +extern struct file_operations ext2_xip_file_operations; /* inode.c */ extern struct address_space_operations ext2_aops; +extern struct address_space_operations ext2_aops_xip; extern struct address_space_operations ext2_nobh_aops; /* namei.c */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index f5e86141ec54..a484412fc782 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -55,6 +55,20 @@ struct file_operations ext2_file_operations = { .sendfile = generic_file_sendfile, }; +#ifdef CONFIG_EXT2_FS_XIP +struct file_operations ext2_xip_file_operations = { + .llseek = generic_file_llseek, + .read = xip_file_read, + .write = xip_file_write, + .ioctl = ext2_ioctl, + .mmap = xip_file_mmap, + .open = generic_file_open, + .release = ext2_release_file, + .fsync = ext2_sync_file, + .sendfile = xip_file_sendfile, +}; +#endif + struct inode_operations ext2_file_inode_operations = { .truncate = ext2_truncate, #ifdef CONFIG_EXT2_FS_XATTR diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index a50d9db4b6e4..53dceb0c6593 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -33,6 +33,7 @@ #include #include "ext2.h" #include "acl.h" +#include "xip.h" MODULE_AUTHOR("Remy Card and others"); MODULE_DESCRIPTION("Second Extended Filesystem"); @@ -594,6 +595,16 @@ out: if (err) goto cleanup; + if (ext2_use_xip(inode->i_sb)) { + /* + * we need to clear the block + */ + err = ext2_clear_xip_target (inode, + le32_to_cpu(chain[depth-1].key)); + if (err) + goto cleanup; + } + if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0) goto changed; @@ -691,6 +702,11 @@ struct address_space_operations ext2_aops = { .writepages = ext2_writepages, }; +struct address_space_operations ext2_aops_xip = { + .bmap = ext2_bmap, + .get_xip_page = ext2_get_xip_page, +}; + struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, @@ -910,7 +926,9 @@ void ext2_truncate (struct inode * inode) iblock = (inode->i_size + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); - if (test_opt(inode->i_sb, NOBH)) + if (mapping_is_xip(inode->i_mapping)) + xip_truncate_page(inode->i_mapping, inode->i_size); + else if (test_opt(inode->i_sb, NOBH)) nobh_truncate_page(inode->i_mapping, inode->i_size); else block_truncate_page(inode->i_mapping, @@ -1110,11 +1128,16 @@ void ext2_read_inode (struct inode * inode) if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; - inode->i_fop = &ext2_file_operations; - if (test_opt(inode->i_sb, NOBH)) + if (ext2_use_xip(inode->i_sb)) { + inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_fop = &ext2_xip_file_operations; + } else if (test_opt(inode->i_sb, NOBH)) { inode->i_mapping->a_ops = &ext2_nobh_aops; - else + inode->i_fop = &ext2_file_operations; + } else { inode->i_mapping->a_ops = &ext2_aops; + inode->i_fop = &ext2_file_operations; + } } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3176b3d3ffa8..c5513953c825 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -34,6 +34,7 @@ #include "ext2.h" #include "xattr.h" #include "acl.h" +#include "xip.h" /* * Couple of helper functions - make the code slightly cleaner. @@ -127,11 +128,16 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, st int err = PTR_ERR(inode); if (!IS_ERR(inode)) { inode->i_op = &ext2_file_inode_operations; - inode->i_fop = &ext2_file_operations; - if (test_opt(inode->i_sb, NOBH)) + if (ext2_use_xip(inode->i_sb)) { + inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_fop = &ext2_xip_file_operations; + } else if (test_opt(inode->i_sb, NOBH)) { inode->i_mapping->a_ops = &ext2_nobh_aops; - else + inode->i_fop = &ext2_file_operations; + } else { inode->i_mapping->a_ops = &ext2_aops; + inode->i_fop = &ext2_file_operations; + } mark_inode_dirty(inode); err = ext2_add_nondir(dentry, inode); } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 661c3d98d946..876e391f2871 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -31,6 +31,7 @@ #include "ext2.h" #include "xattr.h" #include "acl.h" +#include "xip.h" static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es); @@ -257,7 +258,7 @@ enum { Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh, - Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, + Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, }; @@ -286,6 +287,7 @@ static match_table_t tokens = { {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, {Opt_noacl, "noacl"}, + {Opt_xip, "xip"}, {Opt_ignore, "grpquota"}, {Opt_ignore, "noquota"}, {Opt_ignore, "quota"}, @@ -397,6 +399,13 @@ static int parse_options (char * options, printk("EXT2 (no)acl options not supported\n"); break; #endif + case Opt_xip: +#ifdef CONFIG_EXT2_FS_XIP + set_opt (sbi->s_mount_opt, XIP); +#else + printk("EXT2 xip option not supported\n"); +#endif + break; case Opt_ignore: break; default: @@ -640,6 +649,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset + EXT2_MOUNT_XIP if not */ + if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -668,6 +680,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); + if ((ext2_use_xip(sb)) && ((blocksize != PAGE_SIZE) || + (sb->s_blocksize != blocksize))) { + if (!silent) + printk("XIP: Unsupported blocksize\n"); + goto failed_mount; + } + /* If the blocksize doesn't match, re-read the thing.. */ if (sb->s_blocksize != blocksize) { brelse(bh); @@ -916,6 +935,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) { struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_super_block * es; + unsigned long old_mount_opt = sbi->s_mount_opt; /* * Allow the "check" option to be passed as a remount option. @@ -927,6 +947,11 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); es = sbi->s_es; + if (((sbi->s_mount_opt & EXT2_MOUNT_XIP) != + (old_mount_opt & EXT2_MOUNT_XIP)) && + invalidate_inodes(sb)) + ext2_warning(sb, __FUNCTION__, "busy inodes while remounting "\ + "xip remain in cache (no functional problem)"); if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) { diff --git a/fs/ext2/xip.c b/fs/ext2/xip.c new file mode 100644 index 000000000000..d44431d1a338 --- /dev/null +++ b/fs/ext2/xip.c @@ -0,0 +1,80 @@ +/* + * linux/fs/ext2/xip.c + * + * Copyright (C) 2005 IBM Corporation + * Author: Carsten Otte (cotte@de.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include "ext2.h" +#include "xip.h" + +static inline int +__inode_direct_access(struct inode *inode, sector_t sector, unsigned long *data) { + BUG_ON(!inode->i_sb->s_bdev->bd_disk->fops->direct_access); + return inode->i_sb->s_bdev->bd_disk->fops + ->direct_access(inode->i_sb->s_bdev,sector,data); +} + +int +ext2_clear_xip_target(struct inode *inode, int block) { + sector_t sector = block*(PAGE_SIZE/512); + unsigned long data; + int rc; + + rc = __inode_direct_access(inode, sector, &data); + if (rc) + return rc; + clear_page((void*)data); + return 0; +} + +void ext2_xip_verify_sb(struct super_block *sb) +{ + struct ext2_sb_info *sbi = EXT2_SB(sb); + + if ((sbi->s_mount_opt & EXT2_MOUNT_XIP)) { + if ((sb->s_bdev == NULL) || + sb->s_bdev->bd_disk == NULL || + sb->s_bdev->bd_disk->fops == NULL || + sb->s_bdev->bd_disk->fops->direct_access == NULL) { + sbi->s_mount_opt &= (~EXT2_MOUNT_XIP); + ext2_warning(sb, __FUNCTION__, + "ignoring xip option - not supported by bdev"); + } + } +} + +struct page* +ext2_get_xip_page(struct address_space *mapping, sector_t blockno, + int create) +{ + int rc; + unsigned long data; + struct buffer_head tmp; + + tmp.b_state = 0; + tmp.b_blocknr = 0; + rc = ext2_get_block(mapping->host, blockno/(PAGE_SIZE/512) , &tmp, + create); + if (rc) + return ERR_PTR(rc); + if (tmp.b_blocknr == 0) { + /* SPARSE block */ + BUG_ON(create); + return ERR_PTR(-ENODATA); + } + + rc = __inode_direct_access + (mapping->host,tmp.b_blocknr*(PAGE_SIZE/512) ,&data); + if (rc) + return ERR_PTR(rc); + + SetPageUptodate(virt_to_page(data)); + return virt_to_page(data); +} diff --git a/fs/ext2/xip.h b/fs/ext2/xip.h new file mode 100644 index 000000000000..aa85331d6c56 --- /dev/null +++ b/fs/ext2/xip.h @@ -0,0 +1,25 @@ +/* + * linux/fs/ext2/xip.h + * + * Copyright (C) 2005 IBM Corporation + * Author: Carsten Otte (cotte@de.ibm.com) + */ + +#ifdef CONFIG_EXT2_FS_XIP +extern void ext2_xip_verify_sb (struct super_block *); +extern int ext2_clear_xip_target (struct inode *, int); + +static inline int ext2_use_xip (struct super_block *sb) +{ + struct ext2_sb_info *sbi = EXT2_SB(sb); + return (sbi->s_mount_opt & EXT2_MOUNT_XIP); +} +struct page* ext2_get_xip_page (struct address_space *, sector_t, int); +#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_page) +#else +#define mapping_is_xip(map) 0 +#define ext2_xip_verify_sb(sb) do { } while (0) +#define ext2_use_xip(sb) 0 +#define ext2_clear_xip_target(inode, chain) 0 +#define ext2_get_xip_page NULL +#endif diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index 638c13a26c03..3ac38266fc9e 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -393,7 +393,8 @@ ext3_acl_chmod(struct inode *inode) int retries = 0; retry: - handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS); + handle = ext3_journal_start(inode, + EXT3_DATA_TRANS_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) { error = PTR_ERR(handle); ext3_std_error(inode->i_sb, error); @@ -417,12 +418,12 @@ static size_t ext3_xattr_list_acl_access(struct inode *inode, char *list, size_t list_len, const char *name, size_t name_len) { - const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); + const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS); if (!test_opt(inode->i_sb, POSIX_ACL)) return 0; if (list && size <= list_len) - memcpy(list, XATTR_NAME_ACL_ACCESS, size); + memcpy(list, POSIX_ACL_XATTR_ACCESS, size); return size; } @@ -430,12 +431,12 @@ static size_t ext3_xattr_list_acl_default(struct inode *inode, char *list, size_t list_len, const char *name, size_t name_len) { - const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); + const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT); if (!test_opt(inode->i_sb, POSIX_ACL)) return 0; if (list && size <= list_len) - memcpy(list, XATTR_NAME_ACL_DEFAULT, size); + memcpy(list, POSIX_ACL_XATTR_DEFAULT, size); return size; } @@ -503,7 +504,7 @@ ext3_xattr_set_acl(struct inode *inode, int type, const void *value, acl = NULL; retry: - handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS); + handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); error = ext3_set_acl(handle, inode, type, acl); @@ -535,14 +536,14 @@ ext3_xattr_set_acl_default(struct inode *inode, const char *name, } struct xattr_handler ext3_xattr_acl_access_handler = { - .prefix = XATTR_NAME_ACL_ACCESS, + .prefix = POSIX_ACL_XATTR_ACCESS, .list = ext3_xattr_list_acl_access, .get = ext3_xattr_get_acl_access, .set = ext3_xattr_set_acl_access, }; struct xattr_handler ext3_xattr_acl_default_handler = { - .prefix = XATTR_NAME_ACL_DEFAULT, + .prefix = POSIX_ACL_XATTR_DEFAULT, .list = ext3_xattr_list_acl_default, .get = ext3_xattr_get_acl_default, .set = ext3_xattr_set_acl_default, diff --git a/fs/ext3/acl.h b/fs/ext3/acl.h index 98af0c0d0ba9..92d50b53a933 100644 --- a/fs/ext3/acl.h +++ b/fs/ext3/acl.h @@ -4,7 +4,7 @@ (C) 2001 Andreas Gruenbacher, */ -#include +#include #define EXT3_ACL_VERSION 0x0001 diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 0d5fa73b18dc..0b2db4f618cb 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -128,7 +128,7 @@ static unsigned long blocks_for_truncate(struct inode *inode) if (needed > EXT3_MAX_TRANS_DATA) needed = EXT3_MAX_TRANS_DATA; - return EXT3_DATA_TRANS_BLOCKS + needed; + return EXT3_DATA_TRANS_BLOCKS(inode->i_sb) + needed; } /* @@ -2763,7 +2763,8 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) /* (user+group)*(old+new) structure, inode write (sb, * inode block, ? - but truncate inode update has it) */ - handle = ext3_journal_start(inode, 4*EXT3_QUOTA_INIT_BLOCKS+3); + handle = ext3_journal_start(inode, 2*(EXT3_QUOTA_INIT_BLOCKS(inode->i_sb)+ + EXT3_QUOTA_DEL_BLOCKS(inode->i_sb))+3); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto err_out; @@ -2861,7 +2862,7 @@ static int ext3_writepage_trans_blocks(struct inode *inode) #ifdef CONFIG_QUOTA /* We know that structure was already allocated during DQUOT_INIT so * we will be updating only the data blocks + inodes */ - ret += 2*EXT3_QUOTA_TRANS_BLOCKS; + ret += 2*EXT3_QUOTA_TRANS_BLOCKS(inode->i_sb); #endif return ret; diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 79742d824a0a..50378d8ff84b 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -932,8 +932,16 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry, struct inode *dir = dentry->d_parent->d_inode; sb = dir->i_sb; - if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) - return NULL; + /* NFS may look up ".." - look at dx_root directory block */ + if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ + if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) + return NULL; + } else { + frame = frames; + frame->bh = NULL; /* for dx_release() */ + frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ + dx_set_block(frame->at, 0); /* dx_root block is 0 */ + } hash = hinfo.hash; do { block = dx_get_block(frame->at); @@ -1637,9 +1645,9 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode, int err, retries = 0; retry: - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1671,9 +1679,9 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry, return -EINVAL; retry: - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1707,9 +1715,9 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode) return -EMLINK; retry: - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1998,7 +2006,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry) /* Initialize quotas before so that eventual writes go in * separate transaction */ DQUOT_INIT(dentry->d_inode); - handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); + handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2057,7 +2065,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry) /* Initialize quotas before so that eventual writes go * in separate transaction */ DQUOT_INIT(dentry->d_inode); - handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); + handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2112,9 +2120,9 @@ static int ext3_symlink (struct inode * dir, return -ENAMETOOLONG; retry: - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 + - 2*EXT3_QUOTA_INIT_BLOCKS); + 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2166,7 +2174,7 @@ static int ext3_link (struct dentry * old_dentry, return -EMLINK; retry: - handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2208,7 +2216,8 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, * in separate transaction */ if (new_dentry->d_inode) DQUOT_INIT(new_dentry->d_inode); - handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + + handle = ext3_journal_start(old_dir, 2 * + EXT3_DATA_TRANS_BLOCKS(old_dir->i_sb) + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2); if (IS_ERR(handle)) return PTR_ERR(handle); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 981ccb233ef5..b4b3e8a39131 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -589,7 +589,7 @@ enum { Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, - Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, + Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, Opt_ignore, Opt_barrier, Opt_err, Opt_resize, }; @@ -634,10 +634,10 @@ static match_table_t tokens = { {Opt_grpjquota, "grpjquota=%s"}, {Opt_jqfmt_vfsold, "jqfmt=vfsold"}, {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"}, - {Opt_ignore, "grpquota"}, - {Opt_ignore, "noquota"}, - {Opt_ignore, "quota"}, - {Opt_ignore, "usrquota"}, + {Opt_quota, "grpquota"}, + {Opt_noquota, "noquota"}, + {Opt_quota, "quota"}, + {Opt_quota, "usrquota"}, {Opt_barrier, "barrier=%u"}, {Opt_err, NULL}, {Opt_resize, "resize"}, @@ -876,6 +876,7 @@ set_qf_name: sbi->s_qf_names[qtype] = NULL; return 0; } + set_opt(sbi->s_mount_opt, QUOTA); break; case Opt_offusrjquota: qtype = USRQUOTA; @@ -898,6 +899,17 @@ clear_qf_name: case Opt_jqfmt_vfsv0: sbi->s_jquota_fmt = QFMT_VFS_V0; break; + case Opt_quota: + set_opt(sbi->s_mount_opt, QUOTA); + break; + case Opt_noquota: + if (sb_any_quota_enabled(sb)) { + printk(KERN_ERR "EXT3-fs: Cannot change quota " + "options when quota turned on.\n"); + return 0; + } + clear_opt(sbi->s_mount_opt, QUOTA); + break; #else case Opt_usrjquota: case Opt_grpjquota: @@ -909,6 +921,9 @@ clear_qf_name: "EXT3-fs: journalled quota options not " "supported.\n"); break; + case Opt_quota: + case Opt_noquota: + break; #endif case Opt_abort: set_opt(sbi->s_mount_opt, ABORT); @@ -2238,7 +2253,7 @@ static int ext3_dquot_initialize(struct inode *inode, int type) int ret, err; /* We may create quota structure so we need to reserve enough blocks */ - handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS); + handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_initialize(inode, type); @@ -2254,7 +2269,7 @@ static int ext3_dquot_drop(struct inode *inode) int ret, err; /* We may delete quota structure so we need to reserve enough blocks */ - handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS); + handle = ext3_journal_start(inode, 2*EXT3_QUOTA_DEL_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_drop(inode); @@ -2272,7 +2287,7 @@ static int ext3_write_dquot(struct dquot *dquot) inode = dquot_to_inode(dquot); handle = ext3_journal_start(inode, - EXT3_QUOTA_TRANS_BLOCKS); + EXT3_QUOTA_TRANS_BLOCKS(dquot->dq_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_commit(dquot); @@ -2288,7 +2303,7 @@ static int ext3_acquire_dquot(struct dquot *dquot) handle_t *handle; handle = ext3_journal_start(dquot_to_inode(dquot), - EXT3_QUOTA_INIT_BLOCKS); + EXT3_QUOTA_INIT_BLOCKS(dquot->dq_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_acquire(dquot); @@ -2304,7 +2319,7 @@ static int ext3_release_dquot(struct dquot *dquot) handle_t *handle; handle = ext3_journal_start(dquot_to_inode(dquot), - EXT3_QUOTA_INIT_BLOCKS); + EXT3_QUOTA_DEL_BLOCKS(dquot->dq_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_release(dquot); @@ -2348,22 +2363,8 @@ static int ext3_write_info(struct super_block *sb, int type) */ static int ext3_quota_on_mount(struct super_block *sb, int type) { - int err; - struct dentry *dentry; - struct qstr name = { .name = EXT3_SB(sb)->s_qf_names[type], - .hash = 0, - .len = strlen(EXT3_SB(sb)->s_qf_names[type])}; - - dentry = lookup_hash(&name, sb->s_root); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - err = vfs_quota_on_mount(type, EXT3_SB(sb)->s_jquota_fmt, dentry); - /* Now invalidate and put the dentry - quota got its own reference - * to inode and dentry has at least wrong hash so we had better - * throw it away */ - d_invalidate(dentry); - dput(dentry); - return err; + return vfs_quota_on_mount(sb, EXT3_SB(sb)->s_qf_names[type], + EXT3_SB(sb)->s_jquota_fmt, type); } /* @@ -2375,6 +2376,8 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, int err; struct nameidata nd; + if (!test_opt(sb, QUOTA)) + return -EINVAL; /* Not journalling quota? */ if (!EXT3_SB(sb)->s_qf_names[USRQUOTA] && !EXT3_SB(sb)->s_qf_names[GRPQUOTA]) diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index 4cbc6d0212d3..3f9dfa643b19 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -1044,7 +1044,7 @@ ext3_xattr_set(struct inode *inode, int name_index, const char *name, int error, retries = 0; retry: - handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS); + handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) { error = PTR_ERR(handle); } else { diff --git a/fs/file_table.c b/fs/file_table.c index 03d83cb686b1..fa7849fae134 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -63,42 +63,45 @@ static inline void file_free(struct file *f) */ struct file *get_empty_filp(void) { -static int old_max; + static int old_max; struct file * f; /* * Privileged users can go above max_files */ - if (files_stat.nr_files < files_stat.max_files || - capable(CAP_SYS_ADMIN)) { - f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); - if (f) { - memset(f, 0, sizeof(*f)); - if (security_file_alloc(f)) { - file_free(f); - goto fail; - } - eventpoll_init_file(f); - atomic_set(&f->f_count, 1); - f->f_uid = current->fsuid; - f->f_gid = current->fsgid; - rwlock_init(&f->f_owner.lock); - /* f->f_version: 0 */ - INIT_LIST_HEAD(&f->f_list); - f->f_maxcount = INT_MAX; - return f; - } - } + if (files_stat.nr_files >= files_stat.max_files && + !capable(CAP_SYS_ADMIN)) + goto over; + f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); + if (f == NULL) + goto fail; + + memset(f, 0, sizeof(*f)); + if (security_file_alloc(f)) + goto fail_sec; + + eventpoll_init_file(f); + atomic_set(&f->f_count, 1); + f->f_uid = current->fsuid; + f->f_gid = current->fsgid; + rwlock_init(&f->f_owner.lock); + /* f->f_version: 0 */ + INIT_LIST_HEAD(&f->f_list); + f->f_maxcount = INT_MAX; + return f; + +over: /* Ran out of filps - report that */ - if (files_stat.max_files >= old_max) { + if (files_stat.nr_files > old_max) { printk(KERN_INFO "VFS: file-max limit %d reached\n", files_stat.max_files); - old_max = files_stat.max_files; - } else { - /* Big problems... */ - printk(KERN_WARNING "VFS: filp allocation failed\n"); + old_max = files_stat.nr_files; } + goto fail; + +fail_sec: + file_free(f); fail: return NULL; } diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 8e050fa58218..e94ab398b717 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -485,32 +485,6 @@ static void set_sb_syncing(int val) spin_unlock(&sb_lock); } -/* - * Find a superblock with inodes that need to be synced - */ -static struct super_block *get_super_to_sync(void) -{ - struct super_block *sb; -restart: - spin_lock(&sb_lock); - sb = sb_entry(super_blocks.prev); - for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.prev)) { - if (sb->s_syncing) - continue; - sb->s_syncing = 1; - sb->s_count++; - spin_unlock(&sb_lock); - down_read(&sb->s_umount); - if (!sb->s_root) { - drop_super(sb); - goto restart; - } - return sb; - } - spin_unlock(&sb_lock); - return NULL; -} - /** * sync_inodes - writes all inodes to disk * @wait: wait for completion @@ -530,23 +504,39 @@ restart: * outstanding dirty inodes, the writeback goes block-at-a-time within the * filesystem's write_inode(). This is extremely slow. */ -void sync_inodes(int wait) +static void __sync_inodes(int wait) { struct super_block *sb; - set_sb_syncing(0); - while ((sb = get_super_to_sync()) != NULL) { - sync_inodes_sb(sb, 0); - sync_blockdev(sb->s_bdev); - drop_super(sb); + spin_lock(&sb_lock); +restart: + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_syncing) + continue; + sb->s_syncing = 1; + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + if (sb->s_root) { + sync_inodes_sb(sb, wait); + sync_blockdev(sb->s_bdev); + } + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; } + spin_unlock(&sb_lock); +} + +void sync_inodes(int wait) +{ + set_sb_syncing(0); + __sync_inodes(0); + if (wait) { set_sb_syncing(0); - while ((sb = get_super_to_sync()) != NULL) { - sync_inodes_sb(sb, 1); - sync_blockdev(sb->s_bdev); - drop_super(sb); - } + __sync_inodes(1); } } diff --git a/fs/inode.c b/fs/inode.c index 801fe7f36280..1f9a3a2b89bc 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -500,7 +500,7 @@ repeat: continue; if (!test(inode, data)) continue; - if (inode->i_state & (I_FREEING|I_CLEAR)) { + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { __wait_on_freeing_inode(inode); goto repeat; } @@ -525,7 +525,7 @@ repeat: continue; if (inode->i_sb != sb) continue; - if (inode->i_state & (I_FREEING|I_CLEAR)) { + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { __wait_on_freeing_inode(inode); goto repeat; } @@ -727,7 +727,7 @@ EXPORT_SYMBOL(iunique); struct inode *igrab(struct inode *inode) { spin_lock(&inode_lock); - if (!(inode->i_state & I_FREEING)) + if (!(inode->i_state & (I_FREEING|I_WILL_FREE))) __iget(inode); else /* @@ -1024,17 +1024,21 @@ static void generic_forget_inode(struct inode *inode) if (!(inode->i_state & (I_DIRTY|I_LOCK))) list_move(&inode->i_list, &inode_unused); inodes_stat.nr_unused++; - spin_unlock(&inode_lock); - if (!sb || (sb->s_flags & MS_ACTIVE)) + if (!sb || (sb->s_flags & MS_ACTIVE)) { + spin_unlock(&inode_lock); return; + } + inode->i_state |= I_WILL_FREE; + spin_unlock(&inode_lock); write_inode_now(inode, 1); spin_lock(&inode_lock); + inode->i_state &= ~I_WILL_FREE; inodes_stat.nr_unused--; hlist_del_init(&inode->i_hash); } list_del_init(&inode->i_list); list_del_init(&inode->i_sb_list); - inode->i_state|=I_FREEING; + inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); if (inode->i_data.nrpages) diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index 30a2bf9eeda5..e892dab40c26 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "jfs_incore.h" #include "jfs_xattr.h" #include "jfs_acl.h" @@ -36,11 +37,11 @@ static struct posix_acl *jfs_get_acl(struct inode *inode, int type) switch(type) { case ACL_TYPE_ACCESS: - ea_name = XATTR_NAME_ACL_ACCESS; + ea_name = POSIX_ACL_XATTR_ACCESS; p_acl = &ji->i_acl; break; case ACL_TYPE_DEFAULT: - ea_name = XATTR_NAME_ACL_DEFAULT; + ea_name = POSIX_ACL_XATTR_DEFAULT; p_acl = &ji->i_default_acl; break; default: @@ -88,11 +89,11 @@ static int jfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) switch(type) { case ACL_TYPE_ACCESS: - ea_name = XATTR_NAME_ACL_ACCESS; + ea_name = POSIX_ACL_XATTR_ACCESS; p_acl = &ji->i_acl; break; case ACL_TYPE_DEFAULT: - ea_name = XATTR_NAME_ACL_DEFAULT; + ea_name = POSIX_ACL_XATTR_DEFAULT; p_acl = &ji->i_default_acl; if (!S_ISDIR(inode->i_mode)) return acl ? -EACCES : 0; @@ -101,7 +102,7 @@ static int jfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) return -EINVAL; } if (acl) { - size = xattr_acl_size(acl->a_count); + size = posix_acl_xattr_size(acl->a_count); value = kmalloc(size, GFP_KERNEL); if (!value) return -ENOMEM; diff --git a/fs/jfs/jfs_acl.h b/fs/jfs/jfs_acl.h index d2ae430adecf..a3acd3eec059 100644 --- a/fs/jfs/jfs_acl.h +++ b/fs/jfs/jfs_acl.h @@ -20,8 +20,6 @@ #ifdef CONFIG_JFS_POSIX_ACL -#include - int jfs_permission(struct inode *, int, struct nameidata *); int jfs_init_acl(struct inode *, struct inode *); int jfs_setattr(struct dentry *, struct iattr *); diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 810a3653d8b3..ee32211288ce 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "jfs_incore.h" diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 6016373701a3..ee438d429d45 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -19,6 +19,7 @@ #include #include +#include #include #include "jfs_incore.h" #include "jfs_superblock.h" @@ -718,9 +719,9 @@ static int can_set_system_xattr(struct inode *inode, const char *name, return -EPERM; /* - * XATTR_NAME_ACL_ACCESS is tied to i_mode + * POSIX_ACL_XATTR_ACCESS is tied to i_mode */ - if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) { + if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) { acl = posix_acl_from_xattr(value, value_len); if (IS_ERR(acl)) { rc = PTR_ERR(acl); @@ -750,7 +751,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name, JFS_IP(inode)->i_acl = JFS_ACL_NOT_CACHED; return 0; - } else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) { + } else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) { acl = posix_acl_from_xattr(value, value_len); if (IS_ERR(acl)) { rc = PTR_ERR(acl); diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index b82e470912e8..6e242556b903 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -191,7 +191,9 @@ lockd(struct svc_rqst *rqstp) printk(KERN_DEBUG "lockd: new process, skipping host shutdown\n"); wake_up(&lockd_exit); - + + flush_signals(current); + /* Exit the RPC thread */ svc_exit_thread(rqstp); diff --git a/fs/namei.c b/fs/namei.c index a7f7f44119b3..fa8df81ce8ca 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1577,19 +1577,35 @@ do_link: * * Simple function to lookup and return a dentry and create it * if it doesn't exist. Is SMP-safe. + * + * Returns with nd->dentry->d_inode->i_sem locked. */ struct dentry *lookup_create(struct nameidata *nd, int is_dir) { - struct dentry *dentry; + struct dentry *dentry = ERR_PTR(-EEXIST); down(&nd->dentry->d_inode->i_sem); - dentry = ERR_PTR(-EEXIST); + /* + * Yucky last component or no last component at all? + * (foo/., foo/.., /////) + */ if (nd->last_type != LAST_NORM) goto fail; nd->flags &= ~LOOKUP_PARENT; + + /* + * Do the final lookup. + */ dentry = lookup_hash(&nd->last, nd->dentry); if (IS_ERR(dentry)) goto fail; + + /* + * Special case - lookup gave negative, but... we had foo/bar/ + * From the vfs_mknod() POV we just have a negative dentry - + * all is fine. Let's be bastards - you had / on the end, you've + * been asking for (non-existent) directory. -ENOENT for you. + */ if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode) goto enoent; return dentry; diff --git a/fs/namespace.c b/fs/namespace.c index 3b93e5d750eb..208c079e9fdb 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -337,7 +337,7 @@ int may_umount(struct vfsmount *mnt) EXPORT_SYMBOL(may_umount); -void umount_tree(struct vfsmount *mnt) +static void umount_tree(struct vfsmount *mnt) { struct vfsmount *p; LIST_HEAD(kill); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index d6a30c844de3..6537f2c4ae44 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -751,11 +751,6 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, retval = -EFAULT; if (!access_ok(VERIFY_READ, iov.iov_base, iov.iov_len)) goto out; - if (file->f_error) { - retval = file->f_error; - file->f_error = 0; - goto out; - } retval = -EFBIG; if (limit != RLIM_INFINITY) { if (pos >= limit) { diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile index 9f043f44c92f..ce341dc76d5e 100644 --- a/fs/nfsd/Makefile +++ b/fs/nfsd/Makefile @@ -10,5 +10,5 @@ nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \ - nfs4acl.o nfs4callback.o + nfs4acl.o nfs4callback.o nfs4recover.o nfsd-objs := $(nfsd-y) diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 11ebf6c4aa54..4a2105552ac4 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -125,7 +125,7 @@ static short ace2type(struct nfs4_ace *); static int _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int); static struct posix_acl *_nfsv4_to_posix_one(struct nfs4_acl *, unsigned int); int nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t); -int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *); +static int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *); struct nfs4_acl * nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl, @@ -775,7 +775,7 @@ out_err: return pacl; } -int +static int nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl) { struct list_head *h, *n; diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 634465e9cfc6..583c0710e45e 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -54,7 +54,6 @@ /* declarations */ static void nfs4_cb_null(struct rpc_task *task); -extern spinlock_t recall_lock; /* Index of predefined Linux callback client operations */ @@ -329,12 +328,12 @@ out: .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ } -struct rpc_procinfo nfs4_cb_procedures[] = { +static struct rpc_procinfo nfs4_cb_procedures[] = { PROC(CB_NULL, NULL, enc_cb_null, dec_cb_null), PROC(CB_RECALL, COMPOUND, enc_cb_recall, dec_cb_recall), }; -struct rpc_version nfs_cb_version4 = { +static struct rpc_version nfs_cb_version4 = { .number = 1, .nrprocs = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]), .procs = nfs4_cb_procedures @@ -348,7 +347,7 @@ static struct rpc_version * nfs_cb_version[] = { /* * Use the SETCLIENTID credential */ -struct rpc_cred * +static struct rpc_cred * nfsd4_lookupcred(struct nfs4_client *clp, int taskflags) { struct auth_cred acred; @@ -387,9 +386,7 @@ nfsd4_probe_callback(struct nfs4_client *clp) char hostname[32]; int status; - dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d\n", - cb->cb_parsed, atomic_read(&cb->cb_set)); - if (!cb->cb_parsed || atomic_read(&cb->cb_set)) + if (atomic_read(&cb->cb_set)) return; /* Initialize address */ @@ -427,7 +424,7 @@ nfsd4_probe_callback(struct nfs4_client *clp) * XXX AUTH_UNIX only - need AUTH_GSS.... */ sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr)); - clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX); + clnt = rpc_new_client(xprt, hostname, program, 1, RPC_AUTH_UNIX); if (IS_ERR(clnt)) { dprintk("NFSD: couldn't create callback client\n"); goto out_err; diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 4ba540841cf6..5605a26efc57 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -104,7 +104,7 @@ ent_update(struct ent *new, struct ent *itm) ent_init(new, itm); } -void +static void ent_put(struct cache_head *ch, struct cache_detail *cd) { if (cache_put(ch, cd)) { @@ -186,7 +186,7 @@ warn_no_idmapd(struct cache_detail *detail) static int idtoname_parse(struct cache_detail *, char *, int); static struct ent *idtoname_lookup(struct ent *, int); -struct cache_detail idtoname_cache = { +static struct cache_detail idtoname_cache = { .hash_size = ENT_HASHMAX, .hash_table = idtoname_table, .name = "nfs4.idtoname", @@ -277,7 +277,7 @@ nametoid_hash(struct ent *ent) return hash_str(ent->name, ENT_HASHBITS); } -void +static void nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, int *blen) { @@ -317,9 +317,9 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) } static struct ent *nametoid_lookup(struct ent *, int); -int nametoid_parse(struct cache_detail *, char *, int); +static int nametoid_parse(struct cache_detail *, char *, int); -struct cache_detail nametoid_cache = { +static struct cache_detail nametoid_cache = { .hash_size = ENT_HASHMAX, .hash_table = nametoid_table, .name = "nfs4.nametoid", @@ -330,7 +330,7 @@ struct cache_detail nametoid_cache = { .warn_no_listener = warn_no_idmapd, }; -int +static int nametoid_parse(struct cache_detail *cd, char *buf, int buflen) { struct ent ent, *res; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index e8158741e8b5..d71f14517b9c 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -198,6 +199,11 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open if (status) goto out; switch (open->op_claim_type) { + case NFS4_OPEN_CLAIM_DELEGATE_CUR: + status = nfserr_inval; + if (open->op_create) + goto out; + /* fall through */ case NFS4_OPEN_CLAIM_NULL: /* * (1) set CURRENT_FH to the file being opened, @@ -220,7 +226,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open if (status) goto out; break; - case NFS4_OPEN_CLAIM_DELEGATE_CUR: case NFS4_OPEN_CLAIM_DELEGATE_PREV: printk("NFSD: unsupported OPEN claim type %d\n", open->op_claim_type); @@ -473,26 +478,27 @@ static inline int nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read) { int status; - struct file *filp = NULL; /* no need to check permission - this will be done in nfsd_read() */ + read->rd_filp = NULL; if (read->rd_offset >= OFFSET_MAX) return nfserr_inval; nfs4_lock_state(); /* check stateid */ if ((status = nfs4_preprocess_stateid_op(current_fh, &read->rd_stateid, - CHECK_FH | RD_STATE, &filp))) { + CHECK_FH | RD_STATE, &read->rd_filp))) { dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); goto out; } + if (read->rd_filp) + get_file(read->rd_filp); status = nfs_ok; out: nfs4_unlock_state(); read->rd_rqstp = rqstp; read->rd_fhp = current_fh; - read->rd_filp = filp; return status; } @@ -532,6 +538,8 @@ nfsd4_remove(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_rem { int status; + if (nfs4_in_grace()) + return nfserr_grace; status = nfsd_unlink(rqstp, current_fh, 0, remove->rm_name, remove->rm_namelen); if (status == nfserr_symlink) return nfserr_notdir; @@ -550,6 +558,9 @@ nfsd4_rename(struct svc_rqst *rqstp, struct svc_fh *current_fh, if (!save_fh->fh_dentry) return status; + if (nfs4_in_grace() && !(save_fh->fh_export->ex_flags + & NFSEXP_NOSUBTREECHECK)) + return nfserr_grace; status = nfsd_rename(rqstp, save_fh, rename->rn_sname, rename->rn_snamelen, current_fh, rename->rn_tname, rename->rn_tnamelen); @@ -624,6 +635,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ dprintk("NFSD: nfsd4_write: couldn't process stateid!\n"); goto out; } + if (filp) + get_file(filp); nfs4_unlock_state(); write->wr_bytes_written = write->wr_buflen; @@ -635,6 +648,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ status = nfsd_write(rqstp, current_fh, filp, write->wr_offset, write->wr_vec, write->wr_vlen, write->wr_buflen, &write->wr_how_written); + if (filp) + fput(filp); if (status == nfserr_symlink) status = nfserr_inval; @@ -923,6 +938,9 @@ encode_op: nfs4_put_stateowner(replay_owner); replay_owner = NULL; } + /* XXX Ugh, we need to get rid of this kind of special case: */ + if (op->opnum == OP_READ && op->u.read.rd_filp) + fput(op->u.read.rd_filp); } out: diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c new file mode 100644 index 000000000000..095f1740f3ae --- /dev/null +++ b/fs/nfsd/nfs4recover.c @@ -0,0 +1,431 @@ +/* +* linux/fs/nfsd/nfs4recover.c +* +* Copyright (c) 2004 The Regents of the University of Michigan. +* All rights reserved. +* +* Andy Adamson +* +* 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. +* 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. Neither the name of the University nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* 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 REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define NFSDDBG_FACILITY NFSDDBG_PROC + +/* Globals */ +static struct nameidata rec_dir; +static int rec_dir_init = 0; + +static void +nfs4_save_user(uid_t *saveuid, gid_t *savegid) +{ + *saveuid = current->fsuid; + *savegid = current->fsgid; + current->fsuid = 0; + current->fsgid = 0; +} + +static void +nfs4_reset_user(uid_t saveuid, gid_t savegid) +{ + current->fsuid = saveuid; + current->fsgid = savegid; +} + +static void +md5_to_hex(char *out, char *md5) +{ + int i; + + for (i=0; i<16; i++) { + unsigned char c = md5[i]; + + *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); + *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); + } + *out = '\0'; +} + +int +nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) +{ + struct xdr_netobj cksum; + struct crypto_tfm *tfm; + struct scatterlist sg[1]; + int status = nfserr_resource; + + dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", + clname->len, clname->data); + tfm = crypto_alloc_tfm("md5", 0); + if (tfm == NULL) + goto out; + cksum.len = crypto_tfm_alg_digestsize(tfm); + cksum.data = kmalloc(cksum.len, GFP_KERNEL); + if (cksum.data == NULL) + goto out; + crypto_digest_init(tfm); + + sg[0].page = virt_to_page(clname->data); + sg[0].offset = offset_in_page(clname->data); + sg[0].length = clname->len; + + crypto_digest_update(tfm, sg, 1); + crypto_digest_final(tfm, cksum.data); + + md5_to_hex(dname, cksum.data); + + kfree(cksum.data); + status = nfs_ok; +out: + if (tfm) + crypto_free_tfm(tfm); + return status; +} + +static int +nfsd4_rec_fsync(struct dentry *dentry) +{ + struct file *filp; + int status = nfs_ok; + + dprintk("NFSD: nfs4_fsync_rec_dir\n"); + filp = dentry_open(dget(dentry), mntget(rec_dir.mnt), O_RDWR); + if (IS_ERR(filp)) { + status = PTR_ERR(filp); + goto out; + } + if (filp->f_op && filp->f_op->fsync) + status = filp->f_op->fsync(filp, filp->f_dentry, 0); + fput(filp); +out: + if (status) + printk("nfsd4: unable to sync recovery directory\n"); + return status; +} + +int +nfsd4_create_clid_dir(struct nfs4_client *clp) +{ + char *dname = clp->cl_recdir; + struct dentry *dentry; + uid_t uid; + gid_t gid; + int status; + + dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); + + if (!rec_dir_init || clp->cl_firststate) + return 0; + + nfs4_save_user(&uid, &gid); + + /* lock the parent */ + down(&rec_dir.dentry->d_inode->i_sem); + + dentry = lookup_one_len(dname, rec_dir.dentry, HEXDIR_LEN-1); + if (IS_ERR(dentry)) { + status = PTR_ERR(dentry); + goto out_unlock; + } + status = -EEXIST; + if (dentry->d_inode) { + dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n"); + goto out_put; + } + status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU); +out_put: + dput(dentry); +out_unlock: + up(&rec_dir.dentry->d_inode->i_sem); + if (status == 0) { + clp->cl_firststate = 1; + status = nfsd4_rec_fsync(rec_dir.dentry); + } + nfs4_reset_user(uid, gid); + dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status); + return status; +} + +typedef int (recdir_func)(struct dentry *, struct dentry *); + +struct dentry_list { + struct dentry *dentry; + struct list_head list; +}; + +struct dentry_list_arg { + struct list_head dentries; + struct dentry *parent; +}; + +static int +nfsd4_build_dentrylist(void *arg, const char *name, int namlen, + loff_t offset, ino_t ino, unsigned int d_type) +{ + struct dentry_list_arg *dla = arg; + struct list_head *dentries = &dla->dentries; + struct dentry *parent = dla->parent; + struct dentry *dentry; + struct dentry_list *child; + + if (name && isdotent(name, namlen)) + return nfs_ok; + dentry = lookup_one_len(name, parent, namlen); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + child = kmalloc(sizeof(*child), GFP_KERNEL); + if (child == NULL) + return -ENOMEM; + child->dentry = dentry; + list_add(&child->list, dentries); + return 0; +} + +static int +nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f) +{ + struct file *filp; + struct dentry_list_arg dla = { + .parent = dir, + }; + struct list_head *dentries = &dla.dentries; + struct dentry_list *child; + uid_t uid; + gid_t gid; + int status; + + if (!rec_dir_init) + return 0; + + nfs4_save_user(&uid, &gid); + + filp = dentry_open(dget(dir), mntget(rec_dir.mnt), + O_RDWR); + status = PTR_ERR(filp); + if (IS_ERR(filp)) + goto out; + INIT_LIST_HEAD(dentries); + status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla); + fput(filp); + while (!list_empty(dentries)) { + child = list_entry(dentries->next, struct dentry_list, list); + status = f(dir, child->dentry); + if (status) + goto out; + list_del(&child->list); + dput(child->dentry); + kfree(child); + } +out: + while (!list_empty(dentries)) { + child = list_entry(dentries->next, struct dentry_list, list); + list_del(&child->list); + dput(child->dentry); + kfree(child); + } + nfs4_reset_user(uid, gid); + return status; +} + +static int +nfsd4_remove_clid_file(struct dentry *dir, struct dentry *dentry) +{ + int status; + + if (!S_ISREG(dir->d_inode->i_mode)) { + printk("nfsd4: non-file found in client recovery directory\n"); + return -EINVAL; + } + down(&dir->d_inode->i_sem); + status = vfs_unlink(dir->d_inode, dentry); + up(&dir->d_inode->i_sem); + return status; +} + +static int +nfsd4_clear_clid_dir(struct dentry *dir, struct dentry *dentry) +{ + int status; + + /* For now this directory should already be empty, but we empty it of + * any regular files anyway, just in case the directory was created by + * a kernel from the future.... */ + nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file); + down(&dir->d_inode->i_sem); + status = vfs_rmdir(dir->d_inode, dentry); + up(&dir->d_inode->i_sem); + return status; +} + +static int +nfsd4_unlink_clid_dir(char *name, int namlen) +{ + struct dentry *dentry; + int status; + + dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); + + dentry = lookup_one_len(name, rec_dir.dentry, namlen); + if (IS_ERR(dentry)) { + status = PTR_ERR(dentry); + return status; + } + status = -ENOENT; + if (!dentry->d_inode) + goto out; + + status = nfsd4_clear_clid_dir(rec_dir.dentry, dentry); +out: + dput(dentry); + return status; +} + +void +nfsd4_remove_clid_dir(struct nfs4_client *clp) +{ + uid_t uid; + gid_t gid; + int status; + + if (!rec_dir_init || !clp->cl_firststate) + return; + + nfs4_save_user(&uid, &gid); + status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); + nfs4_reset_user(uid, gid); + if (status == 0) + status = nfsd4_rec_fsync(rec_dir.dentry); + if (status) + printk("NFSD: Failed to remove expired client state directory" + " %.*s\n", HEXDIR_LEN, clp->cl_recdir); + return; +} + +static int +purge_old(struct dentry *parent, struct dentry *child) +{ + int status; + + if (nfs4_has_reclaimed_state(child->d_name.name)) + return nfs_ok; + + status = nfsd4_clear_clid_dir(parent, child); + if (status) + printk("failed to remove client recovery directory %s\n", + child->d_name.name); + /* Keep trying, success or failure: */ + return nfs_ok; +} + +void +nfsd4_recdir_purge_old(void) { + int status; + + if (!rec_dir_init) + return; + status = nfsd4_list_rec_dir(rec_dir.dentry, purge_old); + if (status == 0) + status = nfsd4_rec_fsync(rec_dir.dentry); + if (status) + printk("nfsd4: failed to purge old clients from recovery" + " directory %s\n", rec_dir.dentry->d_name.name); + return; +} + +static int +load_recdir(struct dentry *parent, struct dentry *child) +{ + if (child->d_name.len != HEXDIR_LEN - 1) { + printk("nfsd4: illegal name %s in recovery directory\n", + child->d_name.name); + /* Keep trying; maybe the others are OK: */ + return nfs_ok; + } + nfs4_client_to_reclaim(child->d_name.name); + return nfs_ok; +} + +int +nfsd4_recdir_load(void) { + int status; + + status = nfsd4_list_rec_dir(rec_dir.dentry, load_recdir); + if (status) + printk("nfsd4: failed loading clients from recovery" + " directory %s\n", rec_dir.dentry->d_name.name); + return status; +} + +/* + * Hold reference to the recovery directory. + */ + +void +nfsd4_init_recdir(char *rec_dirname) +{ + uid_t uid = 0; + gid_t gid = 0; + int status; + + printk("NFSD: Using %s as the NFSv4 state recovery directory\n", + rec_dirname); + + BUG_ON(rec_dir_init); + + nfs4_save_user(&uid, &gid); + + status = path_lookup(rec_dirname, LOOKUP_FOLLOW, &rec_dir); + if (status == -ENOENT) + printk("NFSD: recovery directory %s doesn't exist\n", + rec_dirname); + + if (!status) + rec_dir_init = 1; + nfs4_reset_user(uid, gid); +} + +void +nfsd4_shutdown_recdir(void) +{ + if (!rec_dir_init) + return; + rec_dir_init = 0; + path_release(&rec_dir); +} diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 75e8b137580c..89e36526d7f2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -48,39 +48,32 @@ #include #include #include +#include #define NFSDDBG_FACILITY NFSDDBG_PROC /* Globals */ static time_t lease_time = 90; /* default lease time */ -static time_t old_lease_time = 90; /* past incarnation lease time */ -static u32 nfs4_reclaim_init = 0; -time_t boot_time; -static time_t grace_end = 0; +static time_t user_lease_time = 90; +static time_t boot_time; +static int in_grace = 1; static u32 current_clientid = 1; static u32 current_ownerid = 1; static u32 current_fileid = 1; static u32 current_delegid = 1; static u32 nfs4_init; -stateid_t zerostateid; /* bits all 0 */ -stateid_t onestateid; /* bits all 1 */ +static stateid_t zerostateid; /* bits all 0 */ +static stateid_t onestateid; /* bits all 1 */ -/* debug counters */ -u32 list_add_perfile = 0; -u32 list_del_perfile = 0; -u32 add_perclient = 0; -u32 del_perclient = 0; -u32 alloc_file = 0; -u32 free_file = 0; -u32 vfsopen = 0; -u32 vfsclose = 0; -u32 alloc_delegation= 0; -u32 free_delegation= 0; +#define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) +#define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) /* forward declarations */ -struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); +static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); static void release_stateid_lockowners(struct nfs4_stateid *open_stp); +static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; +static void nfs4_set_recdir(char *recdir); /* Locking: * @@ -90,6 +83,11 @@ static void release_stateid_lockowners(struct nfs4_stateid *open_stp); */ static DECLARE_MUTEX(client_sema); +static kmem_cache_t *stateowner_slab = NULL; +static kmem_cache_t *file_slab = NULL; +static kmem_cache_t *stateid_slab = NULL; +static kmem_cache_t *deleg_slab = NULL; + void nfs4_lock_state(void) { @@ -118,16 +116,36 @@ opaque_hashval(const void *ptr, int nbytes) /* forward declarations */ static void release_stateowner(struct nfs4_stateowner *sop); static void release_stateid(struct nfs4_stateid *stp, int flags); -static void release_file(struct nfs4_file *fp); /* * Delegation state */ /* recall_lock protects the del_recall_lru */ -spinlock_t recall_lock; +static spinlock_t recall_lock = SPIN_LOCK_UNLOCKED; static struct list_head del_recall_lru; +static void +free_nfs4_file(struct kref *kref) +{ + struct nfs4_file *fp = container_of(kref, struct nfs4_file, fi_ref); + list_del(&fp->fi_hash); + iput(fp->fi_inode); + kmem_cache_free(file_slab, fp); +} + +static inline void +put_nfs4_file(struct nfs4_file *fi) +{ + kref_put(&fi->fi_ref, free_nfs4_file); +} + +static inline void +get_nfs4_file(struct nfs4_file *fi) +{ + kref_get(&fi->fi_ref); +} + static struct nfs4_delegation * alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_fh *current_fh, u32 type) { @@ -136,13 +154,14 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; dprintk("NFSD alloc_init_deleg\n"); - if ((dp = kmalloc(sizeof(struct nfs4_delegation), - GFP_KERNEL)) == NULL) + dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); + if (dp == NULL) return dp; - INIT_LIST_HEAD(&dp->dl_del_perfile); - INIT_LIST_HEAD(&dp->dl_del_perclnt); + INIT_LIST_HEAD(&dp->dl_perfile); + INIT_LIST_HEAD(&dp->dl_perclnt); INIT_LIST_HEAD(&dp->dl_recall_lru); dp->dl_client = clp; + get_nfs4_file(fp); dp->dl_file = fp; dp->dl_flock = NULL; get_file(stp->st_vfs_file); @@ -160,9 +179,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f current_fh->fh_handle.fh_size); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); - list_add(&dp->dl_del_perfile, &fp->fi_del_perfile); - list_add(&dp->dl_del_perclnt, &clp->cl_del_perclnt); - alloc_delegation++; + list_add(&dp->dl_perfile, &fp->fi_delegations); + list_add(&dp->dl_perclnt, &clp->cl_delegations); return dp; } @@ -171,8 +189,8 @@ nfs4_put_delegation(struct nfs4_delegation *dp) { if (atomic_dec_and_test(&dp->dl_count)) { dprintk("NFSD: freeing dp %p\n",dp); - kfree(dp); - free_delegation++; + put_nfs4_file(dp->dl_file); + kmem_cache_free(deleg_slab, dp); } } @@ -193,15 +211,14 @@ nfs4_close_delegation(struct nfs4_delegation *dp) if (dp->dl_flock) setlease(filp, F_UNLCK, &dp->dl_flock); nfsd_close(filp); - vfsclose++; } /* Called under the state lock. */ static void unhash_delegation(struct nfs4_delegation *dp) { - list_del_init(&dp->dl_del_perfile); - list_del_init(&dp->dl_del_perclnt); + list_del_init(&dp->dl_perfile); + list_del_init(&dp->dl_perclnt); spin_lock(&recall_lock); list_del_init(&dp->dl_recall_lru); spin_unlock(&recall_lock); @@ -220,8 +237,8 @@ unhash_delegation(struct nfs4_delegation *dp) #define clientid_hashval(id) \ ((id) & CLIENT_HASH_MASK) -#define clientstr_hashval(name, namelen) \ - (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK) +#define clientstr_hashval(name) \ + (opaque_hashval((name), 8) & CLIENT_HASH_MASK) /* * reclaim_str_hashtbl[] holds known client info from previous reset/reboot * used in reboot/reset lease grace period processing @@ -331,11 +348,11 @@ expire_client(struct nfs4_client *clp) INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); - while (!list_empty(&clp->cl_del_perclnt)) { - dp = list_entry(clp->cl_del_perclnt.next, struct nfs4_delegation, dl_del_perclnt); + while (!list_empty(&clp->cl_delegations)) { + dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); dprintk("NFSD: expire client. dp %p, fp %p\n", dp, dp->dl_flock); - list_del_init(&dp->dl_del_perclnt); + list_del_init(&dp->dl_perclnt); list_move(&dp->dl_recall_lru, &reaplist); } spin_unlock(&recall_lock); @@ -347,26 +364,26 @@ expire_client(struct nfs4_client *clp) list_del(&clp->cl_idhash); list_del(&clp->cl_strhash); list_del(&clp->cl_lru); - while (!list_empty(&clp->cl_perclient)) { - sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient); + while (!list_empty(&clp->cl_openowners)) { + sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); release_stateowner(sop); } put_nfs4_client(clp); } static struct nfs4_client * -create_client(struct xdr_netobj name) { +create_client(struct xdr_netobj name, char *recdir) { struct nfs4_client *clp; if (!(clp = alloc_client(name))) goto out; + memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); atomic_set(&clp->cl_count, 1); atomic_set(&clp->cl_callback.cb_set, 0); - clp->cl_callback.cb_parsed = 0; INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_strhash); - INIT_LIST_HEAD(&clp->cl_perclient); - INIT_LIST_HEAD(&clp->cl_del_perclnt); + INIT_LIST_HEAD(&clp->cl_openowners); + INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_lru); out: return clp; @@ -392,11 +409,9 @@ copy_cred(struct svc_cred *target, struct svc_cred *source) { get_group_info(target->cr_group_info); } -static int -cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { - if (!n1 || !n2) - return 0; - return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len)); +static inline int +same_name(const char *n1, const char *n2) { + return 0 == memcmp(n1, n2, HEXDIR_LEN); } static int @@ -446,7 +461,7 @@ check_name(struct xdr_netobj name) { return 1; } -void +static void add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) { unsigned int idhashval; @@ -458,7 +473,7 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) clp->cl_time = get_seconds(); } -void +static void move_to_confirmed(struct nfs4_client *clp) { unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); @@ -468,8 +483,7 @@ move_to_confirmed(struct nfs4_client *clp) list_del_init(&clp->cl_strhash); list_del_init(&clp->cl_idhash); list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); - strhashval = clientstr_hashval(clp->cl_name.data, - clp->cl_name.len); + strhashval = clientstr_hashval(clp->cl_recdir); list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); renew_client(clp); } @@ -500,6 +514,30 @@ find_unconfirmed_client(clientid_t *clid) return NULL; } +static struct nfs4_client * +find_confirmed_client_by_str(const char *dname, unsigned int hashval) +{ + struct nfs4_client *clp; + + list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { + if (same_name(clp->cl_recdir, dname)) + return clp; + } + return NULL; +} + +static struct nfs4_client * +find_unconfirmed_client_by_str(const char *dname, unsigned int hashval) +{ + struct nfs4_client *clp; + + list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { + if (same_name(clp->cl_recdir, dname)) + return clp; + } + return NULL; +} + /* a helper function for parse_callback */ static int parse_octet(unsigned int *lenp, char **addrp) @@ -534,7 +572,7 @@ parse_octet(unsigned int *lenp, char **addrp) } /* parse and set the setclientid ipv4 callback address */ -int +static int parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp) { int temp = 0; @@ -570,7 +608,7 @@ parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigne return 1; } -void +static void gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) { struct nfs4_callback *cb = &clp->cl_callback; @@ -584,14 +622,12 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se) goto out_err; cb->cb_prog = se->se_callback_prog; cb->cb_ident = se->se_callback_ident; - cb->cb_parsed = 1; return; out_err: printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) " "will not receive delegations\n", clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); - cb->cb_parsed = 0; return; } @@ -638,59 +674,43 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) }; nfs4_verifier clverifier = setclid->se_verf; unsigned int strhashval; - struct nfs4_client * conf, * unconf, * new, * clp; + struct nfs4_client *conf, *unconf, *new; int status; + char dname[HEXDIR_LEN]; status = nfserr_inval; if (!check_name(clname)) goto out; + status = nfs4_make_rec_clidname(dname, &clname); + if (status) + goto out; + /* * XXX The Duplicate Request Cache (DRC) has been checked (??) * We get here on a DRC miss. */ - strhashval = clientstr_hashval(clname.data, clname.len); + strhashval = clientstr_hashval(dname); - conf = NULL; nfs4_lock_state(); - list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) { - if (!cmp_name(&clp->cl_name, &clname)) - continue; + conf = find_confirmed_client_by_str(dname, strhashval); + if (conf) { /* * CASE 0: * clname match, confirmed, different principal * or different ip_address */ status = nfserr_clid_inuse; - if (!cmp_creds(&clp->cl_cred,&rqstp->rq_cred)) { + if (!cmp_creds(&conf->cl_cred, &rqstp->rq_cred) + || conf->cl_addr != ip_addr) { printk("NFSD: setclientid: string in use by client" "(clientid %08x/%08x)\n", - clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); + conf->cl_clientid.cl_boot, conf->cl_clientid.cl_id); goto out; } - if (clp->cl_addr != ip_addr) { - printk("NFSD: setclientid: string in use by client" - "(clientid %08x/%08x)\n", - clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); - goto out; - } - - /* - * cl_name match from a previous SETCLIENTID operation - * XXX check for additional matches? - */ - conf = clp; - break; - } - unconf = NULL; - list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) { - if (!cmp_name(&clp->cl_name, &clname)) - continue; - /* cl_name match from a previous SETCLIENTID operation */ - unconf = clp; - break; } + unconf = find_unconfirmed_client_by_str(dname, strhashval); status = nfserr_resource; if (!conf) { /* @@ -699,7 +719,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) */ if (unconf) expire_client(unconf); - if (!(new = create_client(clname))) + new = create_client(clname, dname); + if (new == NULL) goto out; copy_verf(new, &clverifier); new->cl_addr = ip_addr; @@ -722,12 +743,16 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) * nfs4_client, but with the new callback info and a * new cl_confirm */ - if ((unconf) && - cmp_verf(&unconf->cl_verifier, &conf->cl_verifier) && - cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) { - expire_client(unconf); + if (unconf) { + /* Note this is removing unconfirmed {*x***}, + * which is stronger than RFC recommended {vxc**}. + * This has the advantage that there is at most + * one {*x***} in either list at any time. + */ + expire_client(unconf); } - if (!(new = create_client(clname))) + new = create_client(clname, dname); + if (new == NULL) goto out; copy_verf(new,&conf->cl_verifier); new->cl_addr = ip_addr; @@ -745,7 +770,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) * using input clverifier, clname, and callback info * and generate a new cl_clientid and cl_confirm. */ - if (!(new = create_client(clname))) + new = create_client(clname, dname); + if (new == NULL) goto out; copy_verf(new,&clverifier); new->cl_addr = ip_addr; @@ -771,7 +797,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) * new cl_verifier and a new cl_confirm */ expire_client(unconf); - if (!(new = create_client(clname))) + new = create_client(clname, dname); + if (new == NULL) goto out; copy_verf(new,&clverifier); new->cl_addr = ip_addr; @@ -807,7 +834,7 @@ int nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confirm *setclientid_confirm) { u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr; - struct nfs4_client *clp, *conf = NULL, *unconf = NULL; + struct nfs4_client *conf, *unconf; nfs4_verifier confirm = setclientid_confirm->sc_confirm; clientid_t * clid = &setclientid_confirm->sc_clientid; int status; @@ -820,102 +847,90 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi */ nfs4_lock_state(); - clp = find_confirmed_client(clid); - if (clp) { - status = nfserr_inval; - /* - * Found a record for this clientid. If the IP addresses - * don't match, return ERR_INVAL just as if the record had - * not been found. - */ - if (clp->cl_addr != ip_addr) { - printk("NFSD: setclientid: string in use by client" - "(clientid %08x/%08x)\n", - clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); - goto out; - } - conf = clp; - } - clp = find_unconfirmed_client(clid); - if (clp) { - status = nfserr_inval; - if (clp->cl_addr != ip_addr) { - printk("NFSD: setclientid: string in use by client" - "(clientid %08x/%08x)\n", - clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); - goto out; - } - unconf = clp; - } - /* CASE 1: - * unconf record that matches input clientid and input confirm. - * conf record that matches input clientid. - * conf and unconf records match names, verifiers - */ + + conf = find_confirmed_client(clid); + unconf = find_unconfirmed_client(clid); + + status = nfserr_clid_inuse; + if (conf && conf->cl_addr != ip_addr) + goto out; + if (unconf && unconf->cl_addr != ip_addr) + goto out; + if ((conf && unconf) && (cmp_verf(&unconf->cl_confirm, &confirm)) && (cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) && - (cmp_name(&conf->cl_name,&unconf->cl_name)) && + (same_name(conf->cl_recdir,unconf->cl_recdir)) && (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) { + /* CASE 1: + * unconf record that matches input clientid and input confirm. + * conf record that matches input clientid. + * conf and unconf records match names, verifiers + */ if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred)) status = nfserr_clid_inuse; else { - expire_client(conf); - clp = unconf; - move_to_confirmed(unconf); + /* XXX: We just turn off callbacks until we can handle + * change request correctly. */ + atomic_set(&conf->cl_callback.cb_set, 0); + gen_confirm(conf); + expire_client(unconf); status = nfs_ok; + } - goto out; - } - /* CASE 2: - * conf record that matches input clientid. - * if unconf record that matches input clientid, then unconf->cl_name - * or unconf->cl_verifier don't match the conf record. - */ - if ((conf && !unconf) || + } else if ((conf && !unconf) || ((conf && unconf) && (!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) || - !cmp_name(&conf->cl_name, &unconf->cl_name)))) { - if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) { + !same_name(conf->cl_recdir, unconf->cl_recdir)))) { + /* CASE 2: + * conf record that matches input clientid. + * if unconf record matches input clientid, then + * unconf->cl_name or unconf->cl_verifier don't match the + * conf record. + */ + if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) status = nfserr_clid_inuse; - } else { - clp = conf; + else status = nfs_ok; - } - goto out; - } - /* CASE 3: - * conf record not found. - * unconf record found. - * unconf->cl_confirm matches input confirm - */ - if (!conf && unconf && cmp_verf(&unconf->cl_confirm, &confirm)) { + } else if (!conf && unconf + && cmp_verf(&unconf->cl_confirm, &confirm)) { + /* CASE 3: + * conf record not found. + * unconf record found. + * unconf->cl_confirm matches input confirm + */ if (!cmp_creds(&unconf->cl_cred, &rqstp->rq_cred)) { status = nfserr_clid_inuse; } else { - status = nfs_ok; - clp = unconf; + unsigned int hash = + clientstr_hashval(unconf->cl_recdir); + conf = find_confirmed_client_by_str(unconf->cl_recdir, + hash); + if (conf) { + nfsd4_remove_clid_dir(conf); + expire_client(conf); + } move_to_confirmed(unconf); + conf = unconf; + status = nfs_ok; } - goto out; - } - /* CASE 4: - * conf record not found, or if conf, then conf->cl_confirm does not - * match input confirm. - * unconf record not found, or if unconf, then unconf->cl_confirm - * does not match input confirm. - */ - if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm))) && - (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm, &confirm)))) { + } else if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm))) + && (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm, + &confirm)))) { + /* CASE 4: + * conf record not found, or if conf, conf->cl_confirm does not + * match input confirm. + * unconf record not found, or if unconf, unconf->cl_confirm + * does not match input confirm. + */ status = nfserr_stale_clientid; - goto out; + } else { + /* check that we have hit one of the cases...*/ + status = nfserr_clid_inuse; } - /* check that we have hit one of the cases...*/ - status = nfserr_inval; - goto out; out: if (!status) - nfsd4_probe_callback(clp); + nfsd4_probe_callback(conf); nfs4_unlock_state(); return status; } @@ -961,60 +976,65 @@ alloc_init_file(struct inode *ino) struct nfs4_file *fp; unsigned int hashval = file_hashval(ino); - if ((fp = kmalloc(sizeof(struct nfs4_file),GFP_KERNEL))) { + fp = kmem_cache_alloc(file_slab, GFP_KERNEL); + if (fp) { + kref_init(&fp->fi_ref); INIT_LIST_HEAD(&fp->fi_hash); - INIT_LIST_HEAD(&fp->fi_perfile); - INIT_LIST_HEAD(&fp->fi_del_perfile); + INIT_LIST_HEAD(&fp->fi_stateids); + INIT_LIST_HEAD(&fp->fi_delegations); list_add(&fp->fi_hash, &file_hashtbl[hashval]); fp->fi_inode = igrab(ino); fp->fi_id = current_fileid++; - alloc_file++; return fp; } return NULL; } static void -release_all_files(void) +nfsd4_free_slab(kmem_cache_t **slab) { - int i; - struct nfs4_file *fp; + int status; - for (i=0;ifi_perfile) || !list_empty(&fp->fi_del_perfile)) { - printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp); - } - release_file(fp); - } - } + if (*slab == NULL) + return; + status = kmem_cache_destroy(*slab); + *slab = NULL; + WARN_ON(status); } -kmem_cache_t *stateowner_slab = NULL; +static void +nfsd4_free_slabs(void) +{ + nfsd4_free_slab(&stateowner_slab); + nfsd4_free_slab(&file_slab); + nfsd4_free_slab(&stateid_slab); + nfsd4_free_slab(&deleg_slab); +} static int nfsd4_init_slabs(void) { stateowner_slab = kmem_cache_create("nfsd4_stateowners", sizeof(struct nfs4_stateowner), 0, 0, NULL, NULL); - if (stateowner_slab == NULL) { - dprintk("nfsd4: out of memory while initializing nfsv4\n"); - return -ENOMEM; - } + if (stateowner_slab == NULL) + goto out_nomem; + file_slab = kmem_cache_create("nfsd4_files", + sizeof(struct nfs4_file), 0, 0, NULL, NULL); + if (file_slab == NULL) + goto out_nomem; + stateid_slab = kmem_cache_create("nfsd4_stateids", + sizeof(struct nfs4_stateid), 0, 0, NULL, NULL); + if (stateid_slab == NULL) + goto out_nomem; + deleg_slab = kmem_cache_create("nfsd4_delegations", + sizeof(struct nfs4_delegation), 0, 0, NULL, NULL); + if (deleg_slab == NULL) + goto out_nomem; return 0; -} - -static void -nfsd4_free_slabs(void) -{ - int status = 0; - - if (stateowner_slab) - status = kmem_cache_destroy(stateowner_slab); - stateowner_slab = NULL; - BUG_ON(status); +out_nomem: + nfsd4_free_slabs(); + dprintk("nfsd4: out of memory while initializing nfsv4\n"); + return -ENOMEM; } void @@ -1055,14 +1075,13 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); INIT_LIST_HEAD(&sop->so_perclient); - INIT_LIST_HEAD(&sop->so_perfilestate); - INIT_LIST_HEAD(&sop->so_perlockowner); /* not used */ + INIT_LIST_HEAD(&sop->so_stateids); + INIT_LIST_HEAD(&sop->so_perstateid); /* not used */ INIT_LIST_HEAD(&sop->so_close_lru); sop->so_time = 0; list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); - list_add(&sop->so_perclient, &clp->cl_perclient); - add_perclient++; + list_add(&sop->so_perclient, &clp->cl_openowners); sop->so_is_open_owner = 1; sop->so_id = current_ownerid++; sop->so_client = clp; @@ -1080,10 +1099,10 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp) { struct nfs4_stateowner *lock_sop; - while (!list_empty(&open_stp->st_perlockowner)) { - lock_sop = list_entry(open_stp->st_perlockowner.next, - struct nfs4_stateowner, so_perlockowner); - /* list_del(&open_stp->st_perlockowner); */ + while (!list_empty(&open_stp->st_lockowners)) { + lock_sop = list_entry(open_stp->st_lockowners.next, + struct nfs4_stateowner, so_perstateid); + /* list_del(&open_stp->st_lockowners); */ BUG_ON(lock_sop->so_is_open_owner); release_stateowner(lock_sop); } @@ -1096,14 +1115,12 @@ unhash_stateowner(struct nfs4_stateowner *sop) list_del(&sop->so_idhash); list_del(&sop->so_strhash); - if (sop->so_is_open_owner) { + if (sop->so_is_open_owner) list_del(&sop->so_perclient); - del_perclient++; - } - list_del(&sop->so_perlockowner); - while (!list_empty(&sop->so_perfilestate)) { - stp = list_entry(sop->so_perfilestate.next, - struct nfs4_stateid, st_perfilestate); + list_del(&sop->so_perstateid); + while (!list_empty(&sop->so_stateids)) { + stp = list_entry(sop->so_stateids.next, + struct nfs4_stateid, st_perstateowner); if (sop->so_is_open_owner) release_stateid(stp, OPEN_STATE); else @@ -1125,14 +1142,14 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); INIT_LIST_HEAD(&stp->st_hash); - INIT_LIST_HEAD(&stp->st_perfilestate); - INIT_LIST_HEAD(&stp->st_perlockowner); + INIT_LIST_HEAD(&stp->st_perstateowner); + INIT_LIST_HEAD(&stp->st_lockowners); INIT_LIST_HEAD(&stp->st_perfile); list_add(&stp->st_hash, &stateid_hashtbl[hashval]); - list_add(&stp->st_perfilestate, &sop->so_perfilestate); - list_add_perfile++; - list_add(&stp->st_perfile, &fp->fi_perfile); + list_add(&stp->st_perstateowner, &sop->so_stateids); + list_add(&stp->st_perfile, &fp->fi_stateids); stp->st_stateowner = sop; + get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; @@ -1150,30 +1167,20 @@ release_stateid(struct nfs4_stateid *stp, int flags) struct file *filp = stp->st_vfs_file; list_del(&stp->st_hash); - list_del_perfile++; list_del(&stp->st_perfile); - list_del(&stp->st_perfilestate); + list_del(&stp->st_perstateowner); if (flags & OPEN_STATE) { release_stateid_lockowners(stp); stp->st_vfs_file = NULL; nfsd_close(filp); - vfsclose++; } else if (flags & LOCK_STATE) locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner); - kfree(stp); + put_nfs4_file(stp->st_file); + kmem_cache_free(stateid_slab, stp); stp = NULL; } static void -release_file(struct nfs4_file *fp) -{ - free_file++; - list_del(&fp->fi_hash); - iput(fp->fi_inode); - kfree(fp); -} - -void move_to_close_lru(struct nfs4_stateowner *sop) { dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); @@ -1183,11 +1190,10 @@ move_to_close_lru(struct nfs4_stateowner *sop) sop->so_time = get_seconds(); } -void +static void release_state_owner(struct nfs4_stateid *stp, int flag) { struct nfs4_stateowner *sop = stp->st_stateowner; - struct nfs4_file *fp = stp->st_file; dprintk("NFSD: release_state_owner\n"); release_stateid(stp, flag); @@ -1196,12 +1202,8 @@ release_state_owner(struct nfs4_stateid *stp, int flag) * released by the laundromat service after the lease period * to enable us to handle CLOSE replay */ - if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) + if (sop->so_confirmed && list_empty(&sop->so_stateids)) move_to_close_lru(sop); - /* unused nfs4_file's are releseed. XXX slab cache? */ - if (list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) { - release_file(fp); - } } static int @@ -1231,8 +1233,10 @@ find_file(struct inode *ino) struct nfs4_file *fp; list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { - if (fp->fi_inode == ino) + if (fp->fi_inode == ino) { + get_nfs4_file(fp); return fp; + } } return NULL; } @@ -1240,7 +1244,7 @@ find_file(struct inode *ino) #define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0) #define TEST_DENY(x) ((x >= 0 || x < 5)?1:0) -void +static void set_access(unsigned int *access, unsigned long bmap) { int i; @@ -1251,7 +1255,7 @@ set_access(unsigned int *access, unsigned long bmap) { } } -void +static void set_deny(unsigned int *deny, unsigned long bmap) { int i; @@ -1277,25 +1281,30 @@ test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { * Called to check deny when READ with all zero stateid or * WRITE with all zero or all one stateid */ -int +static int nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) { struct inode *ino = current_fh->fh_dentry->d_inode; struct nfs4_file *fp; struct nfs4_stateid *stp; + int ret; dprintk("NFSD: nfs4_share_conflict\n"); fp = find_file(ino); - if (fp) { + if (!fp) + return nfs_ok; + ret = nfserr_share_denied; /* Search for conflicting share reservations */ - list_for_each_entry(stp, &fp->fi_perfile, st_perfile) { - if (test_bit(deny_type, &stp->st_deny_bmap) || - test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) - return nfserr_share_denied; - } + list_for_each_entry(stp, &fp->fi_stateids, st_perfile) { + if (test_bit(deny_type, &stp->st_deny_bmap) || + test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) + goto out; } - return nfs_ok; + ret = nfs_ok; +out: + put_nfs4_file(fp); + return ret; } static inline void @@ -1427,7 +1436,7 @@ int nfsd_change_deleg_cb(struct file_lock **onlist, int arg) return -EAGAIN; } -struct lock_manager_operations nfsd_lease_mng_ops = { +static struct lock_manager_operations nfsd_lease_mng_ops = { .fl_break = nfsd_break_deleg_cb, .fl_release_private = nfsd_release_deleg_cb, .fl_copy_lock = nfsd_copy_lock_deleg_cb, @@ -1526,6 +1535,51 @@ out: return status; } +static inline int +nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) +{ + if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ)) + return nfserr_openmode; + else + return nfs_ok; +} + +static struct nfs4_delegation * +find_delegation_file(struct nfs4_file *fp, stateid_t *stid) +{ + struct nfs4_delegation *dp; + + list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) { + if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) + return dp; + } + return NULL; +} + +static int +nfs4_check_deleg(struct nfs4_file *fp, struct nfsd4_open *open, + struct nfs4_delegation **dp) +{ + int flags; + int status = nfserr_bad_stateid; + + *dp = find_delegation_file(fp, &open->op_delegate_stateid); + if (*dp == NULL) + goto out; + flags = open->op_share_access == NFS4_SHARE_ACCESS_READ ? + RD_STATE : WR_STATE; + status = nfs4_check_delegmode(*dp, flags); + if (status) + *dp = NULL; +out: + if (open->op_claim_type != NFS4_OPEN_CLAIM_DELEGATE_CUR) + return nfs_ok; + if (status) + return status; + open->op_stateowner->so_confirmed = 1; + return nfs_ok; +} + static int nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_stateid **stpp) { @@ -1533,7 +1587,7 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_state int status = nfserr_share_denied; struct nfs4_stateowner *sop = open->op_stateowner; - list_for_each_entry(local, &fp->fi_perfile, st_perfile) { + list_for_each_entry(local, &fp->fi_stateids, st_perfile) { /* ignore lock owners */ if (local->st_stateowner->so_is_open_owner == 0) continue; @@ -1549,25 +1603,37 @@ out: return status; } +static inline struct nfs4_stateid * +nfs4_alloc_stateid(void) +{ + return kmem_cache_alloc(stateid_slab, GFP_KERNEL); +} + static int nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, + struct nfs4_delegation *dp, struct svc_fh *cur_fh, int flags) { struct nfs4_stateid *stp; - int status; - stp = kmalloc(sizeof(struct nfs4_stateid), GFP_KERNEL); + stp = nfs4_alloc_stateid(); if (stp == NULL) return nfserr_resource; - status = nfsd_open(rqstp, cur_fh, S_IFREG, flags, &stp->st_vfs_file); - if (status) { - if (status == nfserr_dropit) - status = nfserr_jukebox; - kfree(stp); - return status; + if (dp) { + get_file(dp->dl_vfs_file); + stp->st_vfs_file = dp->dl_vfs_file; + } else { + int status; + status = nfsd_open(rqstp, cur_fh, S_IFREG, flags, + &stp->st_vfs_file); + if (status) { + if (status == nfserr_dropit) + status = nfserr_jukebox; + kmem_cache_free(stateid_slab, stp); + return status; + } } - vfsopen++; *stpp = stp; return 0; } @@ -1628,6 +1694,7 @@ nfs4_set_claim_prev(struct nfsd4_open *open, int *status) *status = nfserr_reclaim_bad; else { open->op_stateowner->so_confirmed = 1; + open->op_stateowner->so_client->cl_firststate = 1; open->op_stateowner->so_seqid--; } } @@ -1646,14 +1713,30 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta int status, flag = 0; flag = NFS4_OPEN_DELEGATE_NONE; - if (open->op_claim_type != NFS4_OPEN_CLAIM_NULL - || !atomic_read(&cb->cb_set) || !sop->so_confirmed) - goto out; - - if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - flag = NFS4_OPEN_DELEGATE_WRITE; - else - flag = NFS4_OPEN_DELEGATE_READ; + open->op_recall = 0; + switch (open->op_claim_type) { + case NFS4_OPEN_CLAIM_PREVIOUS: + if (!atomic_read(&cb->cb_set)) + open->op_recall = 1; + flag = open->op_delegate_type; + if (flag == NFS4_OPEN_DELEGATE_NONE) + goto out; + break; + case NFS4_OPEN_CLAIM_NULL: + /* Let's not give out any delegations till everyone's + * had the chance to reclaim theirs.... */ + if (nfs4_in_grace()) + goto out; + if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) + goto out; + if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) + flag = NFS4_OPEN_DELEGATE_WRITE; + else + flag = NFS4_OPEN_DELEGATE_READ; + break; + default: + goto out; + } dp = alloc_init_deleg(sop->so_client, stp, fh, flag); if (dp == NULL) { @@ -1687,6 +1770,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta dp->dl_stateid.si_fileid, dp->dl_stateid.si_generation); out: + if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS + && flag == NFS4_OPEN_DELEGATE_NONE + && open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) + printk("NFSD: WARNING: refusing delegation reclaim\n"); open->op_delegate_type = flag; } @@ -1699,6 +1786,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf struct nfs4_file *fp = NULL; struct inode *ino = current_fh->fh_dentry->d_inode; struct nfs4_stateid *stp = NULL; + struct nfs4_delegation *dp = NULL; int status; status = nfserr_inval; @@ -1713,7 +1801,13 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf if (fp) { if ((status = nfs4_check_open(fp, open, &stp))) goto out; + status = nfs4_check_deleg(fp, open, &dp); + if (status) + goto out; } else { + status = nfserr_bad_stateid; + if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) + goto out; status = nfserr_resource; fp = alloc_init_file(ino); if (fp == NULL) @@ -1736,7 +1830,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf flags = MAY_WRITE; else flags = MAY_READ; - if ((status = nfs4_new_open(rqstp, &stp, current_fh, flags))) + status = nfs4_new_open(rqstp, &stp, dp, current_fh, flags); + if (status) goto out; init_stateid(stp, fp, open); status = nfsd4_truncate(rqstp, current_fh, open); @@ -1759,10 +1854,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf stp->st_stateid.si_boot, stp->st_stateid.si_stateownerid, stp->st_stateid.si_fileid, stp->st_stateid.si_generation); out: - /* take the opportunity to clean up unused state */ - if (fp && list_empty(&fp->fi_perfile) && list_empty(&fp->fi_del_perfile)) - release_file(fp); - + if (fp) + put_nfs4_file(fp); /* CLAIM_PREVIOUS has different error returns */ nfs4_set_claim_prev(open, &status); /* @@ -1775,6 +1868,7 @@ out: return status; } +static struct workqueue_struct *laundry_wq; static struct work_struct laundromat_work; static void laundromat_main(void *); static DECLARE_WORK(laundromat_work, laundromat_main, NULL); @@ -1800,7 +1894,7 @@ nfsd4_renew(clientid_t *clid) } renew_client(clp); status = nfserr_cb_path_down; - if (!list_empty(&clp->cl_del_perclnt) + if (!list_empty(&clp->cl_delegations) && !atomic_read(&clp->cl_callback.cb_set)) goto out; status = nfs_ok; @@ -1809,7 +1903,15 @@ out: return status; } -time_t +static void +end_grace(void) +{ + dprintk("NFSD: end of grace period\n"); + nfsd4_recdir_purge_old(); + in_grace = 0; +} + +static time_t nfs4_laundromat(void) { struct nfs4_client *clp; @@ -1823,6 +1925,8 @@ nfs4_laundromat(void) nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); + if (in_grace) + end_grace(); list_for_each_safe(pos, next, &client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { @@ -1833,6 +1937,7 @@ nfs4_laundromat(void) } dprintk("NFSD: purging unused client (clientid %08x)\n", clp->cl_clientid.cl_id); + nfsd4_remove_clid_dir(clp); expire_client(clp); } INIT_LIST_HEAD(&reaplist); @@ -1882,13 +1987,13 @@ laundromat_main(void *not_used) t = nfs4_laundromat(); dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); - schedule_delayed_work(&laundromat_work, t*HZ); + queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); } /* search ownerid_hashtbl[] and close_lru for stateid owner * (stateid->si_stateownerid) */ -struct nfs4_stateowner * +static struct nfs4_stateowner * find_openstateowner_id(u32 st_id, int flags) { struct nfs4_stateowner *local = NULL; @@ -1948,15 +2053,6 @@ out: return status; } -static inline int -nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) -{ - if ((flags & WR_STATE) && (dp->dl_type == NFS4_OPEN_DELEGATE_READ)) - return nfserr_openmode; - else - return nfs_ok; -} - static inline int check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) { @@ -2071,7 +2167,7 @@ out: /* * Checks for sequence id mutating operations. */ -int +static int nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, clientid_t *lockclid) { int status; @@ -2230,6 +2326,8 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs stp->st_stateid.si_stateownerid, stp->st_stateid.si_fileid, stp->st_stateid.si_generation); + + nfsd4_create_clid_dir(sop->so_client); out: if (oc->oc_stateowner) nfs4_get_stateowner(oc->oc_stateowner); @@ -2387,7 +2485,7 @@ static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE]; static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; -struct nfs4_stateid * +static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags) { struct nfs4_stateid *local = NULL; @@ -2419,25 +2517,19 @@ find_stateid(stateid_t *stid, int flags) static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid) { - struct nfs4_delegation *dp = NULL; - struct nfs4_file *fp = NULL; - u32 st_id; + struct nfs4_file *fp; + struct nfs4_delegation *dl; dprintk("NFSD:find_delegation_stateid stateid=(%08x/%08x/%08x/%08x)\n", stid->si_boot, stid->si_stateownerid, stid->si_fileid, stid->si_generation); - st_id = stid->si_stateownerid; fp = find_file(ino); - if (fp) { - list_for_each_entry(dp, &fp->fi_del_perfile, dl_del_perfile) { - if(dp->dl_stateid.si_stateownerid == st_id) { - dprintk("NFSD: find_delegation dp %p\n",dp); - return dp; - } - } - } - return NULL; + if (!fp) + return NULL; + dl = find_delegation_file(fp, stid); + put_nfs4_file(fp); + return dl; } /* @@ -2457,7 +2549,7 @@ nfs4_transform_lock_offset(struct file_lock *lock) lock->fl_end = OFFSET_MAX; } -int +static int nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval) { struct nfs4_stateowner *local = NULL; @@ -2497,22 +2589,6 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) deny->ld_type = NFS4_WRITE_LT; } -static struct nfs4_stateowner * -find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid) -{ - struct nfs4_stateowner *local = NULL; - int i; - - for (i = 0; i < LOCK_HASH_SIZE; i++) { - list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) { - if (!cmp_owner_str(local, owner, clid)) - continue; - return local; - } - } - return NULL; -} - static struct nfs4_stateowner * find_lockstateowner_str(struct inode *inode, clientid_t *clid, struct xdr_netobj *owner) @@ -2548,13 +2624,13 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); INIT_LIST_HEAD(&sop->so_perclient); - INIT_LIST_HEAD(&sop->so_perfilestate); - INIT_LIST_HEAD(&sop->so_perlockowner); + INIT_LIST_HEAD(&sop->so_stateids); + INIT_LIST_HEAD(&sop->so_perstateid); INIT_LIST_HEAD(&sop->so_close_lru); /* not used */ sop->so_time = 0; list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); - list_add(&sop->so_perlockowner, &open_stp->st_perlockowner); + list_add(&sop->so_perstateid, &open_stp->st_lockowners); sop->so_is_open_owner = 0; sop->so_id = current_ownerid++; sop->so_client = clp; @@ -2567,24 +2643,24 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str return sop; } -struct nfs4_stateid * +static struct nfs4_stateid * alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp) { struct nfs4_stateid *stp; unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id); - if ((stp = kmalloc(sizeof(struct nfs4_stateid), - GFP_KERNEL)) == NULL) + stp = nfs4_alloc_stateid(); + if (stp == NULL) goto out; INIT_LIST_HEAD(&stp->st_hash); INIT_LIST_HEAD(&stp->st_perfile); - INIT_LIST_HEAD(&stp->st_perfilestate); - INIT_LIST_HEAD(&stp->st_perlockowner); /* not used */ + INIT_LIST_HEAD(&stp->st_perstateowner); + INIT_LIST_HEAD(&stp->st_lockowners); /* not used */ list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]); - list_add(&stp->st_perfile, &fp->fi_perfile); - list_add_perfile++; - list_add(&stp->st_perfilestate, &sop->so_perfilestate); + list_add(&stp->st_perfile, &fp->fi_stateids); + list_add(&stp->st_perstateowner, &sop->so_stateids); stp->st_stateowner = sop; + get_nfs4_file(fp); stp->st_file = fp; stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; @@ -2598,7 +2674,7 @@ out: return stp; } -int +static int check_lock_length(u64 offset, u64 length) { return ((length == 0) || ((length != ~(u64)0) && @@ -2611,7 +2687,7 @@ check_lock_length(u64 offset, u64 length) int nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock) { - struct nfs4_stateowner *lock_sop = NULL, *open_sop = NULL; + struct nfs4_stateowner *open_sop = NULL; struct nfs4_stateid *lock_stp; struct file *filp; struct file_lock file_lock; @@ -2670,16 +2746,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock strhashval = lock_ownerstr_hashval(fp->fi_inode, open_sop->so_client->cl_clientid.cl_id, &lock->v.new.owner); - /* - * If we already have this lock owner, the client is in - * error (or our bookeeping is wrong!) - * for asking for a 'new lock'. - */ - status = nfserr_bad_stateid; - lock_sop = find_lockstateowner(&lock->v.new.owner, - &lock->v.new.clientid); - if (lock_sop) - goto out; + /* XXX: Do we need to check for duplicate stateowners on + * the same file, or should they just be allowed (and + * create new stateids)? */ status = nfserr_resource; if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) goto out; @@ -2970,8 +3039,11 @@ int nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner) { clientid_t *clid = &rlockowner->rl_clientid; - struct nfs4_stateowner *local = NULL; + struct nfs4_stateowner *sop; + struct nfs4_stateid *stp; struct xdr_netobj *owner = &rlockowner->rl_owner; + struct list_head matches; + int i; int status; dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", @@ -2987,22 +3059,32 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner * nfs4_lock_state(); - status = nfs_ok; - local = find_lockstateowner(owner, clid); - if (local) { - struct nfs4_stateid *stp; - - /* check for any locks held by any stateid - * associated with the (lock) stateowner */ - status = nfserr_locks_held; - list_for_each_entry(stp, &local->so_perfilestate, - st_perfilestate) { - if (check_for_locks(stp->st_vfs_file, local)) - goto out; + status = nfserr_locks_held; + /* XXX: we're doing a linear search through all the lockowners. + * Yipes! For now we'll just hope clients aren't really using + * release_lockowner much, but eventually we have to fix these + * data structures. */ + INIT_LIST_HEAD(&matches); + for (i = 0; i < LOCK_HASH_SIZE; i++) { + list_for_each_entry(sop, &lock_ownerid_hashtbl[i], so_idhash) { + if (!cmp_owner_str(sop, owner, clid)) + continue; + list_for_each_entry(stp, &sop->so_stateids, + st_perstateowner) { + if (check_for_locks(stp->st_vfs_file, sop)) + goto out; + /* Note: so_perclient unused for lockowners, + * so it's OK to fool with here. */ + list_add(&sop->so_perclient, &matches); + } } - /* no locks held by (lock) stateowner */ - status = nfs_ok; - release_stateowner(local); + } + /* Clients probably won't expect us to return with some (but not all) + * of the lockowner state released; so don't release any until all + * have been checked. */ + status = nfs_ok; + list_for_each_entry(sop, &matches, so_perclient) { + release_stateowner(sop); } out: nfs4_unlock_state(); @@ -3010,39 +3092,38 @@ out: } static inline struct nfs4_client_reclaim * -alloc_reclaim(int namelen) +alloc_reclaim(void) { - struct nfs4_client_reclaim *crp = NULL; + return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); +} - crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); - if (!crp) - return NULL; - crp->cr_name.data = kmalloc(namelen, GFP_KERNEL); - if (!crp->cr_name.data) { - kfree(crp); - return NULL; - } - return crp; +int +nfs4_has_reclaimed_state(const char *name) +{ + unsigned int strhashval = clientstr_hashval(name); + struct nfs4_client *clp; + + clp = find_confirmed_client_by_str(name, strhashval); + return clp ? 1 : 0; } /* * failure => all reset bets are off, nfserr_no_grace... */ -static int -nfs4_client_to_reclaim(char *name, int namlen) +int +nfs4_client_to_reclaim(const char *name) { unsigned int strhashval; struct nfs4_client_reclaim *crp = NULL; - dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", namlen, name); - crp = alloc_reclaim(namlen); + dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); + crp = alloc_reclaim(); if (!crp) return 0; - strhashval = clientstr_hashval(name, namlen); + strhashval = clientstr_hashval(name); INIT_LIST_HEAD(&crp->cr_strhash); list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); - memcpy(crp->cr_name.data, name, namlen); - crp->cr_name.len = namlen; + memcpy(crp->cr_recdir, name, HEXDIR_LEN); reclaim_str_hashtbl_size++; return 1; } @@ -3053,13 +3134,11 @@ nfs4_release_reclaim(void) struct nfs4_client_reclaim *crp = NULL; int i; - BUG_ON(!nfs4_reclaim_init); for (i = 0; i < CLIENT_HASH_SIZE; i++) { while (!list_empty(&reclaim_str_hashtbl[i])) { crp = list_entry(reclaim_str_hashtbl[i].next, struct nfs4_client_reclaim, cr_strhash); list_del(&crp->cr_strhash); - kfree(crp->cr_name.data); kfree(crp); reclaim_str_hashtbl_size--; } @@ -3069,7 +3148,7 @@ nfs4_release_reclaim(void) /* * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ -struct nfs4_client_reclaim * +static struct nfs4_client_reclaim * nfs4_find_reclaim_client(clientid_t *clid) { unsigned int strhashval; @@ -3082,13 +3161,14 @@ nfs4_find_reclaim_client(clientid_t *clid) if (clp == NULL) return NULL; - dprintk("NFSD: nfs4_find_reclaim_client for %.*s\n", - clp->cl_name.len, clp->cl_name.data); + dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", + clp->cl_name.len, clp->cl_name.data, + clp->cl_recdir); /* find clp->cl_name in reclaim_str_hashtbl */ - strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len); + strhashval = clientstr_hashval(clp->cl_recdir); list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { - if (cmp_name(&crp->cr_name, &clp->cl_name)) { + if (same_name(crp->cr_recdir, clp->cl_recdir)) { return crp; } } @@ -3101,30 +3181,16 @@ nfs4_find_reclaim_client(clientid_t *clid) int nfs4_check_open_reclaim(clientid_t *clid) { - struct nfs4_client_reclaim *crp; - - if ((crp = nfs4_find_reclaim_client(clid)) == NULL) - return nfserr_reclaim_bad; - return nfs_ok; + return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; } +/* initialization to perform at module load time: */ -/* - * Start and stop routines - */ - -static void -__nfs4_state_init(void) +void +nfs4_state_init(void) { int i; - time_t grace_time; - if (!nfs4_reclaim_init) { - for (i = 0; i < CLIENT_HASH_SIZE; i++) - INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); - reclaim_str_hashtbl_size = 0; - nfs4_reclaim_init = 1; - } for (i = 0; i < CLIENT_HASH_SIZE; i++) { INIT_LIST_HEAD(&conf_id_hashtbl[i]); INIT_LIST_HEAD(&conf_str_hashtbl[i]); @@ -3146,26 +3212,46 @@ __nfs4_state_init(void) INIT_LIST_HEAD(&lock_ownerid_hashtbl[i]); INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); } - memset(&zerostateid, 0, sizeof(stateid_t)); memset(&onestateid, ~0, sizeof(stateid_t)); - INIT_LIST_HEAD(&close_lru); INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&del_recall_lru); - spin_lock_init(&recall_lock); + for (i = 0; i < CLIENT_HASH_SIZE; i++) + INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); + reclaim_str_hashtbl_size = 0; +} + +static void +nfsd4_load_reboot_recovery_data(void) +{ + int status; + + nfs4_lock_state(); + nfsd4_init_recdir(user_recovery_dirname); + status = nfsd4_recdir_load(); + nfs4_unlock_state(); + if (status) + printk("NFSD: Failure reading reboot recovery data\n"); +} + +/* initialization to perform when the nfsd service is started: */ + +static void +__nfs4_state_start(void) +{ + time_t grace_time; + boot_time = get_seconds(); - grace_time = max(old_lease_time, lease_time); - if (reclaim_str_hashtbl_size == 0) - grace_time = 0; - if (grace_time) - printk("NFSD: starting %ld-second grace period\n", grace_time); - grace_end = boot_time + grace_time; - INIT_WORK(&laundromat_work,laundromat_main, NULL); - schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ); + grace_time = max(user_lease_time, lease_time); + lease_time = user_lease_time; + in_grace = 1; + printk("NFSD: starting %ld-second grace period\n", grace_time); + laundry_wq = create_singlethread_workqueue("nfsd4"); + queue_delayed_work(laundry_wq, &laundromat_work, grace_time*HZ); } int -nfs4_state_init(void) +nfs4_state_start(void) { int status; @@ -3174,7 +3260,8 @@ nfs4_state_init(void) status = nfsd4_init_slabs(); if (status) return status; - __nfs4_state_init(); + nfsd4_load_reboot_recovery_data(); + __nfs4_state_start(); nfs4_init = 1; return 0; } @@ -3182,14 +3269,7 @@ nfs4_state_init(void) int nfs4_in_grace(void) { - return get_seconds() < grace_end; -} - -void -set_no_grace(void) -{ - printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n"); - grace_end = get_seconds(); + return in_grace; } time_t @@ -3236,21 +3316,11 @@ __nfs4_state_shutdown(void) unhash_delegation(dp); } - release_all_files(); cancel_delayed_work(&laundromat_work); - flush_scheduled_work(); + flush_workqueue(laundry_wq); + destroy_workqueue(laundry_wq); + nfsd4_shutdown_recdir(); nfs4_init = 0; - dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n", - list_add_perfile, list_del_perfile); - dprintk("NFSD: add_perclient %d del_perclient %d\n", - add_perclient, del_perclient); - dprintk("NFSD: alloc_file %d free_file %d\n", - alloc_file, free_file); - dprintk("NFSD: vfsopen %d vfsclose %d\n", - vfsopen, vfsclose); - dprintk("NFSD: alloc_delegation %d free_delegation %d\n", - alloc_delegation, free_delegation); - } void @@ -3263,56 +3333,48 @@ nfs4_state_shutdown(void) nfs4_unlock_state(); } +static void +nfs4_set_recdir(char *recdir) +{ + nfs4_lock_state(); + strcpy(user_recovery_dirname, recdir); + nfs4_unlock_state(); +} + +/* + * Change the NFSv4 recovery directory to recdir. + */ +int +nfs4_reset_recoverydir(char *recdir) +{ + int status; + struct nameidata nd; + + status = path_lookup(recdir, LOOKUP_FOLLOW, &nd); + if (status) + return status; + status = -ENOTDIR; + if (S_ISDIR(nd.dentry->d_inode->i_mode)) { + nfs4_set_recdir(recdir); + status = 0; + } + path_release(&nd); + return status; +} + /* * Called when leasetime is changed. * - * if nfsd is not started, simply set the global lease. - * - * if nfsd(s) are running, lease change requires nfsv4 state to be reset. - * e.g: boot_time is reset, existing nfs4_client structs are - * used to fill reclaim_str_hashtbl, then all state (except for the - * reclaim_str_hashtbl) is re-initialized. - * - * if the old lease time is greater than the new lease time, the grace - * period needs to be set to the old lease time to allow clients to reclaim - * their state. XXX - we may want to set the grace period == lease time - * after an initial grace period == old lease time - * - * if an error occurs in this process, the new lease is set, but the server - * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace - * which means OPEN/LOCK/READ/WRITE will fail during grace period. - * - * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and - * OPEN and LOCK reclaims. + * The only way the protocol gives us to handle on-the-fly lease changes is to + * simulate a reboot. Instead of doing that, we just wait till the next time + * we start to register any changes in lease time. If the administrator + * really wants to change the lease time *now*, they can go ahead and bring + * nfsd down and then back up again after changing the lease time. */ void nfs4_reset_lease(time_t leasetime) { - struct nfs4_client *clp; - int i; - - printk("NFSD: New leasetime %ld\n",leasetime); - if (!nfs4_init) - return; - nfs4_lock_state(); - old_lease_time = lease_time; - lease_time = leasetime; - - nfs4_release_reclaim(); - - /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */ - for (i = 0; i < CLIENT_HASH_SIZE; i++) { - list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) { - if (!nfs4_client_to_reclaim(clp->cl_name.data, - clp->cl_name.len)) { - nfs4_release_reclaim(); - goto init_state; - } - } - } -init_state: - __nfs4_state_shutdown(); - __nfs4_state_init(); - nfs4_unlock_state(); + lock_kernel(); + user_lease_time = leasetime; + unlock_kernel(); } - diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 36a058a112d5..91fb171d2ace 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -136,7 +136,7 @@ xdr_error: \ } \ } while (0) -u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes) +static u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes) { /* We want more bytes than seem to be available. * Maybe we need a new page, maybe we have just run out @@ -190,7 +190,7 @@ defer_free(struct nfsd4_compoundargs *argp, return 0; } -char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes) +static char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes) { void *new = NULL; if (p == argp->tmp) { @@ -1366,7 +1366,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { if ((buflen -= 4) < 0) goto out_resource; - WRITE32( NFS4_FH_NOEXPIRE_WITH_OPEN | NFS4_FH_VOL_RENAME ); + if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) + WRITE32(NFS4_FH_VOLATILE_ANY); + else + WRITE32(NFS4_FH_VOLATILE_ANY|NFS4_FH_VOL_RENAME); } if (bmval0 & FATTR4_WORD0_CHANGE) { /* @@ -1969,7 +1972,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open case NFS4_OPEN_DELEGATE_READ: RESERVE_SPACE(20 + sizeof(stateid_t)); WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); - WRITE32(0); + WRITE32(open->op_recall); /* * TODO: ACE's in delegations diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 161afdcb8f7d..841c562991e8 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -51,6 +51,7 @@ enum { NFSD_Fh, NFSD_Threads, NFSD_Leasetime, + NFSD_RecoveryDir, }; /* @@ -66,6 +67,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size); static ssize_t write_filehandle(struct file *file, char *buf, size_t size); static ssize_t write_threads(struct file *file, char *buf, size_t size); static ssize_t write_leasetime(struct file *file, char *buf, size_t size); +static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Svc] = write_svc, @@ -78,6 +80,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Fh] = write_filehandle, [NFSD_Threads] = write_threads, [NFSD_Leasetime] = write_leasetime, + [NFSD_RecoveryDir] = write_recoverydir, }; static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) @@ -349,6 +352,25 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) return strlen(buf); } +static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) +{ + char *mesg = buf; + char *recdir; + int len, status; + + if (size > PATH_MAX || buf[size-1] != '\n') + return -EINVAL; + buf[size-1] = 0; + + recdir = mesg; + len = qword_get(&mesg, recdir, size); + if (len <= 0) + return -EINVAL; + + status = nfs4_reset_recoverydir(recdir); + return strlen(buf); +} + /*----------------------------------------------------------------------------*/ /* * populating the filesystem. @@ -369,6 +391,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, + [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, #endif /* last one */ {""} }; @@ -397,9 +420,8 @@ static int __init init_nfsd(void) nfsd_cache_init(); /* RPC reply cache */ nfsd_export_init(); /* Exports table */ nfsd_lockd_init(); /* lockd->nfsd callbacks */ -#ifdef CONFIG_NFSD_V4 + nfs4_state_init(); /* NFSv4 locking state */ nfsd_idmap_init(); /* Name to ID mapping */ -#endif /* CONFIG_NFSD_V4 */ if (proc_mkdir("fs/nfs", NULL)) { struct proc_dir_entry *entry; entry = create_proc_entry("fs/nfs/exports", 0, NULL); @@ -426,9 +448,7 @@ static void __exit exit_nfsd(void) remove_proc_entry("fs/nfs", NULL); nfsd_stat_shutdown(); nfsd_lockd_shutdown(); -#ifdef CONFIG_NFSD_V4 nfsd_idmap_shutdown(); -#endif /* CONFIG_NFSD_V4 */ unregister_filesystem(&nfsd_fs_type); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 904df604e86b..07b9a065e9da 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -95,7 +95,7 @@ nfsd_svc(unsigned short port, int nrservs) error = nfsd_racache_init(2*nrservs); if (error<0) goto out; - error = nfs4_state_init(); + error = nfs4_state_start(); if (error<0) goto out; if (!nfsd_serv) { diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index ae3940dc85cc..de340ffd33c3 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -50,7 +50,6 @@ #include #ifdef CONFIG_NFSD_V4 #include -#include #include #include #include @@ -425,13 +424,13 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out_nfserr; if (pacl) { - error = set_nfsv4_acl_one(dentry, pacl, XATTR_NAME_ACL_ACCESS); + error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); if (error < 0) goto out_nfserr; } if (dpacl) { - error = set_nfsv4_acl_one(dentry, dpacl, XATTR_NAME_ACL_DEFAULT); + error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); if (error < 0) goto out_nfserr; } @@ -498,7 +497,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac struct posix_acl *pacl = NULL, *dpacl = NULL; unsigned int flags = 0; - pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS); + pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS); if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA) pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); if (IS_ERR(pacl)) { @@ -508,7 +507,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac } if (S_ISDIR(inode->i_mode)) { - dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT); + dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT); if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA) dpacl = NULL; else if (IS_ERR(dpacl)) { diff --git a/fs/open.c b/fs/open.c index 963bd81a44c8..3f4a4286fdc4 100644 --- a/fs/open.c +++ b/fs/open.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -807,7 +808,9 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) /* NB: we're sure to have correct a_ops only after f_op->open */ if (f->f_flags & O_DIRECT) { - if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO) { + if (!f->f_mapping->a_ops || + ((!f->f_mapping->a_ops->direct_IO) && + (!f->f_mapping->a_ops->get_xip_page))) { fput(f); f = ERR_PTR(-EINVAL); } @@ -933,31 +936,27 @@ EXPORT_SYMBOL(fd_install); asmlinkage long sys_open(const char __user * filename, int flags, int mode) { char * tmp; - int fd, error; + int fd; + + if (force_o_largefile()) + flags |= O_LARGEFILE; -#if BITS_PER_LONG != 32 - flags |= O_LARGEFILE; -#endif tmp = getname(filename); fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { fd = get_unused_fd(); if (fd >= 0) { struct file *f = filp_open(tmp, flags, mode); - error = PTR_ERR(f); - if (IS_ERR(f)) - goto out_error; - fd_install(fd, f); + if (IS_ERR(f)) { + put_unused_fd(fd); + fd = PTR_ERR(f); + } else { + fd_install(fd, f); + } } -out: putname(tmp); } return fd; - -out_error: - put_unused_fd(fd); - fd = error; - goto out; } EXPORT_SYMBOL_GPL(sys_open); @@ -980,23 +979,15 @@ asmlinkage long sys_creat(const char __user * pathname, int mode) */ int filp_close(struct file *filp, fl_owner_t id) { - int retval; - - /* Report and clear outstanding errors */ - retval = filp->f_error; - if (retval) - filp->f_error = 0; + int retval = 0; if (!file_count(filp)) { printk(KERN_ERR "VFS: Close: file count is 0\n"); - return retval; + return 0; } - if (filp->f_op && filp->f_op->flush) { - int err = filp->f_op->flush(filp); - if (!retval) - retval = err; - } + if (filp->f_op && filp->f_op->flush) + retval = filp->f_op->flush(filp); dnotify_flush(filp, id); locks_remove_posix(filp, id); diff --git a/fs/proc/base.c b/fs/proc/base.c index e31903aadd96..ace151fa4878 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -314,7 +314,7 @@ static int may_ptrace_attach(struct task_struct *task) (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) goto out; rmb(); - if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) + if (task->mm->dumpable != 1 && !capable(CAP_SYS_PTRACE)) goto out; if (security_ptrace(current, task)) goto out; @@ -1113,7 +1113,9 @@ static int task_dumpable(struct task_struct *task) if (mm) dumpable = mm->dumpable; task_unlock(task); - return dumpable; + if(dumpable == 1) + return 1; + return 0; } diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 63a9fbf1ac51..94b570ad037d 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -451,7 +451,7 @@ static int devices_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = get_chrdev_list(page); - len += get_blkdev_list(page+len); + len += get_blkdev_list(page+len, len); return proc_calc_metrics(page, start, off, count, eof, len); } diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index cd66147cca04..7a8f5595c26f 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -61,7 +61,7 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1; else { le = (struct qnx4_link_info*)de; - ino = ( le->dl_inode_blk - 1 ) * + ino = ( le32_to_cpu(le->dl_inode_blk) - 1 ) * QNX4_INODES_PER_BLOCK + le->dl_inode_ndx; } diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index aa92d6b76a9a..b79162a35478 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -236,7 +236,7 @@ unsigned long qnx4_block_map( struct inode *inode, long iblock ) struct buffer_head *bh = NULL; struct qnx4_xblk *xblk = NULL; struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); - qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); + u16 nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) { // iblock is in the first extent. This is easy. @@ -372,7 +372,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) printk("qnx4: unable to read the superblock\n"); goto outnobh; } - if ( le32_to_cpu( *(__u32*)bh->b_data ) != QNX4_SUPER_MAGIC ) { + if ( le32_to_cpup((__le32*) bh->b_data) != QNX4_SUPER_MAGIC ) { if (!silent) printk("qnx4: wrong fsid in superblock.\n"); goto out; diff --git a/fs/quota.c b/fs/quota.c index 3f0333a51a23..f5d1cff55196 100644 --- a/fs/quota.c +++ b/fs/quota.c @@ -149,36 +149,6 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t return error; } -static struct super_block *get_super_to_sync(int type) -{ - struct list_head *head; - int cnt, dirty; - -restart: - spin_lock(&sb_lock); - list_for_each(head, &super_blocks) { - struct super_block *sb = list_entry(head, struct super_block, s_list); - - /* This test just improves performance so it needn't be reliable... */ - for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++) - if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt) - && info_any_dirty(&sb_dqopt(sb)->info[cnt])) - dirty = 1; - if (!dirty) - continue; - sb->s_count++; - spin_unlock(&sb_lock); - down_read(&sb->s_umount); - if (!sb->s_root) { - drop_super(sb); - goto restart; - } - return sb; - } - spin_unlock(&sb_lock); - return NULL; -} - static void quota_sync_sb(struct super_block *sb, int type) { int cnt; @@ -219,17 +189,35 @@ static void quota_sync_sb(struct super_block *sb, int type) void sync_dquots(struct super_block *sb, int type) { + int cnt, dirty; + if (sb) { if (sb->s_qcop->quota_sync) quota_sync_sb(sb, type); + return; } - else { - while ((sb = get_super_to_sync(type)) != NULL) { - if (sb->s_qcop->quota_sync) - quota_sync_sb(sb, type); - drop_super(sb); - } + + spin_lock(&sb_lock); +restart: + list_for_each_entry(sb, &super_blocks, s_list) { + /* This test just improves performance so it needn't be reliable... */ + for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++) + if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt) + && info_any_dirty(&sb_dqopt(sb)->info[cnt])) + dirty = 1; + if (!dirty) + continue; + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + if (sb->s_root && sb->s_qcop->quota_sync) + quota_sync_sb(sb, type); + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; } + spin_unlock(&sb_lock); } /* Copy parameters and call proper function */ diff --git a/fs/read_write.c b/fs/read_write.c index c4c2bee373ed..9292f5fa4d62 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -203,6 +203,16 @@ Einval: return -EINVAL; } +static void wait_on_retry_sync_kiocb(struct kiocb *iocb) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + if (!kiocbIsKicked(iocb)) + schedule(); + else + kiocbClearKicked(iocb); + __set_current_state(TASK_RUNNING); +} + ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { struct kiocb kiocb; @@ -210,7 +220,10 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - ret = filp->f_op->aio_read(&kiocb, buf, len, kiocb.ki_pos); + while (-EIOCBRETRY == + (ret = filp->f_op->aio_read(&kiocb, buf, len, kiocb.ki_pos))) + wait_on_retry_sync_kiocb(&kiocb); + if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; @@ -258,7 +271,10 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - ret = filp->f_op->aio_write(&kiocb, buf, len, kiocb.ki_pos); + while (-EIOCBRETRY == + (ret = filp->f_op->aio_write(&kiocb, buf, len, kiocb.ki_pos))) + wait_on_retry_sync_kiocb(&kiocb); + if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 2230afff1870..12e91209544e 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -201,7 +201,7 @@ static int reiserfs_allocate_blocks_for_region( /* If we came here, it means we absolutely need to open a transaction, since we need to allocate some blocks */ reiserfs_write_lock(inode->i_sb); // Journaling stuff and we need that. - res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS); // Wish I know if this number enough + res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS(inode->i_sb)); // Wish I know if this number enough if (res) goto error_exit; reiserfs_update_inode_transaction(inode) ; @@ -576,7 +576,7 @@ error_exit: int err; // update any changes we made to blk count reiserfs_update_sd(th, inode); - err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS); + err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS(inode->i_sb)); if (err) res = err; } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 2711dff1b7b4..0d5817f81972 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -28,7 +28,7 @@ static int reiserfs_prepare_write(struct file *f, struct page *page, void reiserfs_delete_inode (struct inode * inode) { /* We need blocks for transaction + (user+group) quota update (possibly delete) */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * REISERFS_QUOTA_INIT_BLOCKS; + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb); struct reiserfs_transaction_handle th ; reiserfs_write_lock(inode->i_sb); @@ -591,7 +591,7 @@ int reiserfs_get_block (struct inode * inode, sector_t block, XXX in practically impossible worst case direct2indirect() can incur (much) more than 3 balancings. quota update for user, group */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS; + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS(inode->i_sb); int version; int dangle = 1; loff_t new_offset = (((loff_t)block) << inode->i_sb->s_blocksize_bits) + 1 ; @@ -2796,12 +2796,15 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) { if (!error) { struct reiserfs_transaction_handle th; + int jbegin_count = 2*(REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb)+REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb))+2; /* (user+group)*(old+new) structure - we count quota info and , inode write (sb, inode) */ - journal_begin(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2); + error = journal_begin(&th, inode->i_sb, jbegin_count); + if (error) + goto out; error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; if (error) { - journal_end(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2); + journal_end(&th, inode->i_sb, jbegin_count); goto out; } /* Update corresponding info in inode so that everything is in @@ -2811,7 +2814,7 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) { if (attr->ia_valid & ATTR_GID) inode->i_gid = attr->ia_gid; mark_inode_dirty(inode); - journal_end(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2); + error = journal_end(&th, inode->i_sb, jbegin_count); } } if (!error) diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 3072cfdee959..7b87707acc36 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -2631,6 +2631,8 @@ static int do_journal_begin_r(struct reiserfs_transaction_handle *th, struct sup int retval; reiserfs_check_lock_depth(p_s_sb, "journal_begin") ; + if (nblocks > journal->j_trans_max) + BUG(); PROC_INFO_INC( p_s_sb, journal.journal_being ); /* set here for journal_join */ diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index 7d4dc5f5aa8b..4a333255f27a 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -586,7 +586,7 @@ static int reiserfs_create (struct inode * dir, struct dentry *dentry, int mode, int retval; struct inode * inode; /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS(dir->i_sb)+REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb)); struct reiserfs_transaction_handle th ; int locked; @@ -653,7 +653,7 @@ static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode, struct inode * inode; struct reiserfs_transaction_handle th ; /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS(dir->i_sb)+REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb)); int locked; if (!new_valid_dev(rdev)) @@ -727,7 +727,7 @@ static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode) struct inode * inode; struct reiserfs_transaction_handle th ; /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS(dir->i_sb)+REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb)); int locked; #ifdef DISPLACE_NEW_PACKING_LOCALITIES @@ -829,8 +829,10 @@ static int reiserfs_rmdir (struct inode * dir, struct dentry *dentry) /* we will be doing 2 balancings and update 2 stat data, we change quotas - * of the owner of the directory and of the owner of the parent directory */ - jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); + * of the owner of the directory and of the owner of the parent directory. + * The quota structure is possibly deleted only on last iput => outside + * of this transaction */ + jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 4 * REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb); reiserfs_write_lock(dir->i_sb); retval = journal_begin(&th, dir->i_sb, jbegin_count) ; @@ -913,9 +915,10 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry) inode = dentry->d_inode; /* in this transaction we can be doing at max two balancings and update - two stat datas, we change quotas of the owner of the directory and of - the owner of the parent directory */ - jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); + * two stat datas, we change quotas of the owner of the directory and of + * the owner of the parent directory. The quota structure is possibly + * deleted only on iput => outside of this transaction */ + jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 4 * REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb); reiserfs_write_lock(dir->i_sb); retval = journal_begin(&th, dir->i_sb, jbegin_count) ; @@ -1000,7 +1003,7 @@ static int reiserfs_symlink (struct inode * parent_dir, struct reiserfs_transaction_handle th ; int mode = S_IFLNK | S_IRWXUGO; /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS(parent_dir->i_sb)+REISERFS_QUOTA_TRANS_BLOCKS(parent_dir->i_sb)); if (!(inode = new_inode(parent_dir->i_sb))) { return -ENOMEM ; @@ -1076,7 +1079,7 @@ static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct struct inode *inode = old_dentry->d_inode; struct reiserfs_transaction_handle th ; /* We need blocks for transaction + update of quotas for the owners of the directory */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * REISERFS_QUOTA_TRANS_BLOCKS; + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb); reiserfs_write_lock(dir->i_sb); if (inode->i_nlink >= REISERFS_LINK_MAX) { @@ -1196,7 +1199,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry, pointed initially and (5) maybe block containing ".." of renamed directory quota updates: two parent directories */ - jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 5 + 4 * REISERFS_QUOTA_TRANS_BLOCKS; + jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 5 + 4 * REISERFS_QUOTA_TRANS_BLOCKS(old_dir->i_sb); old_inode = old_dentry->d_inode; new_dentry_inode = new_dentry->d_inode; diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index c47f8fd31a2d..63158491e152 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -223,7 +223,7 @@ extern struct tree_balance * cur_tb; const struct reiserfs_key MIN_KEY = {0, 0, {{0, 0},}}; /* Maximal possible key. It is never in the tree. */ -const struct reiserfs_key MAX_KEY = { +static const struct reiserfs_key MAX_KEY = { __constant_cpu_to_le32(0xffffffff), __constant_cpu_to_le32(0xffffffff), {{__constant_cpu_to_le32(0xffffffff), diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index b35b87744983..660aefca1fd2 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -866,8 +866,9 @@ static int reiserfs_parse_options (struct super_block * s, char * options, /* st {"jdev", .arg_required = 'j', .values = NULL}, {"nolargeio", .arg_required = 'w', .values = NULL}, {"commit", .arg_required = 'c', .values = NULL}, - {"usrquota",}, - {"grpquota",}, + {"usrquota", .setmask = 1<s_qf_names[qtype], arg); + *mount_options |= 1<s_qf_names[qtype]) { @@ -995,7 +997,13 @@ static int reiserfs_parse_options (struct super_block * s, char * options, /* st reiserfs_warning(s, "reiserfs_parse_options: journalled quota format not specified."); return 0; } + /* This checking is not precise wrt the quota type but for our purposes it is sufficient */ + if (!(*mount_options & (1<i_sb); - journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + ret = journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb)); + if (ret) + goto out; ret = dquot_initialize(inode, type); - journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + err = journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb)); + if (!ret && err) + ret = err; +out: reiserfs_write_unlock(inode->i_sb); return ret; } @@ -1855,13 +1869,18 @@ static int reiserfs_dquot_initialize(struct inode *inode, int type) static int reiserfs_dquot_drop(struct inode *inode) { struct reiserfs_transaction_handle th; - int ret; + int ret, err; /* We may delete quota structure so we need to reserve enough blocks */ reiserfs_write_lock(inode->i_sb); - journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + ret = journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb)); + if (ret) + goto out; ret = dquot_drop(inode); - journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + err = journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb)); + if (!ret && err) + ret = err; +out: reiserfs_write_unlock(inode->i_sb); return ret; } @@ -1869,12 +1888,17 @@ static int reiserfs_dquot_drop(struct inode *inode) static int reiserfs_write_dquot(struct dquot *dquot) { struct reiserfs_transaction_handle th; - int ret; + int ret, err; reiserfs_write_lock(dquot->dq_sb); - journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS); + ret = journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS(dquot->dq_sb)); + if (ret) + goto out; ret = dquot_commit(dquot); - journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS); + err = journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS(dquot->dq_sb)); + if (!ret && err) + ret = err; +out: reiserfs_write_unlock(dquot->dq_sb); return ret; } @@ -1882,12 +1906,17 @@ static int reiserfs_write_dquot(struct dquot *dquot) static int reiserfs_acquire_dquot(struct dquot *dquot) { struct reiserfs_transaction_handle th; - int ret; + int ret, err; reiserfs_write_lock(dquot->dq_sb); - journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + ret = journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS(dquot->dq_sb)); + if (ret) + goto out; ret = dquot_acquire(dquot); - journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + err = journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS(dquot->dq_sb)); + if (!ret && err) + ret = err; +out: reiserfs_write_unlock(dquot->dq_sb); return ret; } @@ -1895,12 +1924,17 @@ static int reiserfs_acquire_dquot(struct dquot *dquot) static int reiserfs_release_dquot(struct dquot *dquot) { struct reiserfs_transaction_handle th; - int ret; + int ret, err; reiserfs_write_lock(dquot->dq_sb); - journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + ret = journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_DEL_BLOCKS(dquot->dq_sb)); + if (ret) + goto out; ret = dquot_release(dquot); - journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + err = journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_DEL_BLOCKS(dquot->dq_sb)); + if (!ret && err) + ret = err; +out: reiserfs_write_unlock(dquot->dq_sb); return ret; } @@ -1920,39 +1954,29 @@ static int reiserfs_mark_dquot_dirty(struct dquot *dquot) static int reiserfs_write_info(struct super_block *sb, int type) { struct reiserfs_transaction_handle th; - int ret; + int ret, err; /* Data block + inode block */ reiserfs_write_lock(sb); - journal_begin(&th, sb, 2); + ret = journal_begin(&th, sb, 2); + if (ret) + goto out; ret = dquot_commit_info(sb, type); - journal_end(&th, sb, 2); + err = journal_end(&th, sb, 2); + if (!ret && err) + ret = err; +out: reiserfs_write_unlock(sb); return ret; } /* - * Turn on quotas during mount time - we need to find - * the quota file and such... + * Turn on quotas during mount time - we need to find the quota file and such... */ static int reiserfs_quota_on_mount(struct super_block *sb, int type) { - int err; - struct dentry *dentry; - struct qstr name = { .name = REISERFS_SB(sb)->s_qf_names[type], - .hash = 0, - .len = strlen(REISERFS_SB(sb)->s_qf_names[type])}; - - dentry = lookup_hash(&name, sb->s_root); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - err = vfs_quota_on_mount(type, REISERFS_SB(sb)->s_jquota_fmt, dentry); - /* Now invalidate and put the dentry - quota got its own reference - * to inode and dentry has at least wrong hash so we had better - * throw it away */ - d_invalidate(dentry); - dput(dentry); - return err; + return vfs_quota_on_mount(sb, REISERFS_SB(sb)->s_qf_names[type], + REISERFS_SB(sb)->s_jquota_fmt, type); } /* @@ -1963,6 +1987,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, ch int err; struct nameidata nd; + if (!(REISERFS_SB(sb)->s_mount_opt & (1< #include #include -#include +#include #include #include #include @@ -192,11 +192,11 @@ reiserfs_get_acl(struct inode *inode, int type) switch (type) { case ACL_TYPE_ACCESS: - name = XATTR_NAME_ACL_ACCESS; + name = POSIX_ACL_XATTR_ACCESS; p_acl = &reiserfs_i->i_acl_access; break; case ACL_TYPE_DEFAULT: - name = XATTR_NAME_ACL_DEFAULT; + name = POSIX_ACL_XATTR_DEFAULT; p_acl = &reiserfs_i->i_acl_default; break; default: @@ -260,7 +260,7 @@ reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) switch (type) { case ACL_TYPE_ACCESS: - name = XATTR_NAME_ACL_ACCESS; + name = POSIX_ACL_XATTR_ACCESS; p_acl = &reiserfs_i->i_acl_access; if (acl) { mode_t mode = inode->i_mode; @@ -275,7 +275,7 @@ reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) } break; case ACL_TYPE_DEFAULT: - name = XATTR_NAME_ACL_DEFAULT; + name = POSIX_ACL_XATTR_DEFAULT; p_acl = &reiserfs_i->i_acl_default; if (!S_ISDIR (inode->i_mode)) return acl ? -EACCES : 0; @@ -468,7 +468,7 @@ static int posix_acl_access_get(struct inode *inode, const char *name, void *buffer, size_t size) { - if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1) + if (strlen(name) != sizeof(POSIX_ACL_XATTR_ACCESS)-1) return -EINVAL; return xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); } @@ -477,7 +477,7 @@ static int posix_acl_access_set(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1) + if (strlen(name) != sizeof(POSIX_ACL_XATTR_ACCESS)-1) return -EINVAL; return xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); } @@ -487,7 +487,7 @@ posix_acl_access_del (struct inode *inode, const char *name) { struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode); struct posix_acl **acl = &reiserfs_i->i_acl_access; - if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1) + if (strlen(name) != sizeof(POSIX_ACL_XATTR_ACCESS)-1) return -EINVAL; if (!IS_ERR (*acl) && *acl) { posix_acl_release (*acl); @@ -510,7 +510,7 @@ posix_acl_access_list (struct inode *inode, const char *name, int namelen, char } struct reiserfs_xattr_handler posix_acl_access_handler = { - .prefix = XATTR_NAME_ACL_ACCESS, + .prefix = POSIX_ACL_XATTR_ACCESS, .get = posix_acl_access_get, .set = posix_acl_access_set, .del = posix_acl_access_del, @@ -521,7 +521,7 @@ static int posix_acl_default_get (struct inode *inode, const char *name, void *buffer, size_t size) { - if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1) + if (strlen(name) != sizeof(POSIX_ACL_XATTR_DEFAULT)-1) return -EINVAL; return xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); } @@ -530,7 +530,7 @@ static int posix_acl_default_set(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1) + if (strlen(name) != sizeof(POSIX_ACL_XATTR_DEFAULT)-1) return -EINVAL; return xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); } @@ -540,7 +540,7 @@ posix_acl_default_del (struct inode *inode, const char *name) { struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode); struct posix_acl **acl = &reiserfs_i->i_acl_default; - if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1) + if (strlen(name) != sizeof(POSIX_ACL_XATTR_DEFAULT)-1) return -EINVAL; if (!IS_ERR (*acl) && *acl) { posix_acl_release (*acl); @@ -563,7 +563,7 @@ posix_acl_default_list (struct inode *inode, const char *name, int namelen, char } struct reiserfs_xattr_handler posix_acl_default_handler = { - .prefix = XATTR_NAME_ACL_DEFAULT, + .prefix = POSIX_ACL_XATTR_DEFAULT, .get = posix_acl_default_get, .set = posix_acl_default_set, .del = posix_acl_default_del, diff --git a/fs/super.c b/fs/super.c index 573bcc81bb82..25bc1ec6bc5d 100644 --- a/fs/super.c +++ b/fs/super.c @@ -341,20 +341,22 @@ static inline void write_super(struct super_block *sb) */ void sync_supers(void) { - struct super_block * sb; -restart: + struct super_block *sb; + spin_lock(&sb_lock); - sb = sb_entry(super_blocks.next); - while (sb != sb_entry(&super_blocks)) +restart: + list_for_each_entry(sb, &super_blocks, s_list) { if (sb->s_dirt) { sb->s_count++; spin_unlock(&sb_lock); down_read(&sb->s_umount); write_super(sb); - drop_super(sb); - goto restart; - } else - sb = sb_entry(sb->s_list.next); + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; + } + } spin_unlock(&sb_lock); } @@ -381,20 +383,16 @@ void sync_filesystems(int wait) down(&mutex); /* Could be down_interruptible */ spin_lock(&sb_lock); - for (sb = sb_entry(super_blocks.next); sb != sb_entry(&super_blocks); - sb = sb_entry(sb->s_list.next)) { + list_for_each_entry(sb, &super_blocks, s_list) { if (!sb->s_op->sync_fs) continue; if (sb->s_flags & MS_RDONLY) continue; sb->s_need_sync_fs = 1; } - spin_unlock(&sb_lock); restart: - spin_lock(&sb_lock); - for (sb = sb_entry(super_blocks.next); sb != sb_entry(&super_blocks); - sb = sb_entry(sb->s_list.next)) { + list_for_each_entry(sb, &super_blocks, s_list) { if (!sb->s_need_sync_fs) continue; sb->s_need_sync_fs = 0; @@ -405,8 +403,11 @@ restart: down_read(&sb->s_umount); if (sb->s_root && (wait || sb->s_dirt)) sb->s_op->sync_fs(sb, wait); - drop_super(sb); - goto restart; + up_read(&sb->s_umount); + /* restart only when sb is no longer on the list */ + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; } spin_unlock(&sb_lock); up(&mutex); @@ -422,21 +423,25 @@ restart: struct super_block * get_super(struct block_device *bdev) { - struct list_head *p; + struct super_block *sb; + if (!bdev) return NULL; -rescan: + spin_lock(&sb_lock); - list_for_each(p, &super_blocks) { - struct super_block *s = sb_entry(p); - if (s->s_bdev == bdev) { - s->s_count++; +rescan: + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_bdev == bdev) { + sb->s_count++; spin_unlock(&sb_lock); - down_read(&s->s_umount); - if (s->s_root) - return s; - drop_super(s); - goto rescan; + down_read(&sb->s_umount); + if (sb->s_root) + return sb; + up_read(&sb->s_umount); + /* restart only when sb is no longer on the list */ + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto rescan; } } spin_unlock(&sb_lock); @@ -447,20 +452,22 @@ EXPORT_SYMBOL(get_super); struct super_block * user_get_super(dev_t dev) { - struct list_head *p; + struct super_block *sb; -rescan: spin_lock(&sb_lock); - list_for_each(p, &super_blocks) { - struct super_block *s = sb_entry(p); - if (s->s_dev == dev) { - s->s_count++; +rescan: + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_dev == dev) { + sb->s_count++; spin_unlock(&sb_lock); - down_read(&s->s_umount); - if (s->s_root) - return s; - drop_super(s); - goto rescan; + down_read(&sb->s_umount); + if (sb->s_root) + return sb; + up_read(&sb->s_umount); + /* restart only when sb is no longer on the list */ + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto rescan; } } spin_unlock(&sb_lock); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 37d7a6875d86..59734ba1ee60 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "sysfs.h" DECLARE_RWSEM(sysfs_rename_sem); @@ -99,7 +100,7 @@ static int create_dir(struct kobject * k, struct dentry * p, umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; down(&p->d_inode->i_sem); - *d = sysfs_get_dentry(p,n); + *d = lookup_one_len(n, p, strlen(n)); if (!IS_ERR(*d)) { error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, SYSFS_DIR); if (!error) { @@ -315,7 +316,7 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name) down(&parent->d_inode->i_sem); - new_dentry = sysfs_get_dentry(parent, new_name); + new_dentry = lookup_one_len(new_name, parent, strlen(new_name)); if (!IS_ERR(new_dentry)) { if (!new_dentry->d_inode) { error = kobject_set_name(kobj, "%s", new_name); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 849aac115460..d72c1ce48559 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,7 +14,7 @@ #define to_subsys(k) container_of(k,struct subsystem,kset.kobj) #define to_sattr(a) container_of(a,struct subsys_attribute,attr) -/** +/* * Subsystem file operations. * These operations allow subsystems to have files that can be * read/written. @@ -191,8 +192,9 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t /** * flush_write_buffer - push buffer to kobject. - * @file: file pointer. + * @dentry: dentry to the attribute * @buffer: data buffer for file. + * @count: number of bytes * * Get the correct pointers for the kobject and the attribute we're * dealing with, then call the store() method for the attribute, @@ -400,7 +402,7 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) int res = -ENOENT; down(&dir->d_inode->i_sem); - victim = sysfs_get_dentry(dir, attr->name); + victim = lookup_one_len(attr->name, dir, strlen(attr->name)); if (!IS_ERR(victim)) { /* make sure dentry is really there */ if (victim->d_inode && @@ -443,7 +445,7 @@ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) int res = -ENOENT; down(&dir->d_inode->i_sem); - victim = sysfs_get_dentry(dir, attr->name); + victim = lookup_one_len(attr->name, dir, strlen(attr->name)); if (!IS_ERR(victim)) { if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index f11ac5ea7021..122145b0895c 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "sysfs.h" @@ -68,7 +69,8 @@ void sysfs_remove_group(struct kobject * kobj, struct dentry * dir; if (grp->name) - dir = sysfs_get_dentry(kobj->dentry,grp->name); + dir = lookup_one_len(grp->name, kobj->dentry, + strlen(grp->name)); else dir = dget(kobj->dentry); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 565cac1d4200..8de13bafaa76 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -166,16 +166,6 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) return error; } -struct dentry * sysfs_get_dentry(struct dentry * parent, const char * name) -{ - struct qstr qstr; - - qstr.name = name; - qstr.len = strlen(name); - qstr.hash = full_name_hash(name,qstr.len); - return lookup_hash(&qstr,parent); -} - /* * Get the name for corresponding element represented by the given sysfs_dirent */ diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 29da6f5f07c8..3f8953e0e5d0 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -7,7 +7,6 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, umode_t, int); -extern struct dentry * sysfs_get_dentry(struct dentry *, const char *); extern int sysfs_add_file(struct dentry *, const struct attribute *, int); extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 93ce257cd149..a3a4b5aaf5d9 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -149,11 +149,12 @@ linvfs_unwritten_convert( */ STATIC void linvfs_unwritten_convert_direct( - struct inode *inode, + struct kiocb *iocb, loff_t offset, ssize_t size, void *private) { + struct inode *inode = iocb->ki_filp->f_dentry->d_inode; ASSERT(!private || inode == (struct inode *)private); /* private indicates an unwritten extent lay beneath this IO */ diff --git a/include/asm-alpha/mmzone.h b/include/asm-alpha/mmzone.h index 726c150dcbe4..a011ef4cf3d3 100644 --- a/include/asm-alpha/mmzone.h +++ b/include/asm-alpha/mmzone.h @@ -57,7 +57,6 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) * Given a kernel address, find the home node of the underlying memory. */ #define kvaddr_to_nid(kaddr) pa_to_nid(__pa(kaddr)) -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define local_mapnr(kvaddr) \ @@ -108,7 +107,7 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) #define pfn_to_page(pfn) \ ({ \ unsigned long kaddr = (unsigned long)__va((pfn) << PAGE_SHIFT); \ - (node_mem_map(kvaddr_to_nid(kaddr)) + local_mapnr(kaddr)); \ + (NODE_DATA(kvaddr_to_nid(kaddr))->node_mem_map + local_mapnr(kaddr)); \ }) #define page_to_pfn(page) \ diff --git a/include/asm-arm/arch-ixp2000/gpio.h b/include/asm-arm/arch-ixp2000/gpio.h index 84634af5cc64..03cbbe1fd9d8 100644 --- a/include/asm-arm/arch-ixp2000/gpio.h +++ b/include/asm-arm/arch-ixp2000/gpio.h @@ -1,5 +1,5 @@ /* - * include/asm-arm/arch-ixp2000/ixp2000-gpio.h + * include/asm-arm/arch-ixp2000/gpio.h * * Copyright (C) 2002 Intel Corporation. * @@ -16,26 +16,18 @@ * Use this instead of directly setting the GPIO registers. * GPIOs may also be used as GPIOs (e.g. for emulating i2c/smb) */ -#ifndef _ASM_ARCH_IXP2000_GPIO_H_ -#define _ASM_ARCH_IXP2000_GPIO_H_ +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H #ifndef __ASSEMBLY__ -#define GPIO_OUT 0x0 -#define GPIO_IN 0x80 + +#define GPIO_IN 0 +#define GPIO_OUT 1 #define IXP2000_GPIO_LOW 0 #define IXP2000_GPIO_HIGH 1 -#define GPIO_NO_EDGES 0 -#define GPIO_FALLING_EDGE 1 -#define GPIO_RISING_EDGE 2 -#define GPIO_BOTH_EDGES 3 -#define GPIO_LEVEL_LOW 4 -#define GPIO_LEVEL_HIGH 8 - -extern void set_GPIO_IRQ_edge(int gpio_nr, int edge); -extern void set_GPIO_IRQ_level(int gpio_nr, int level); -extern void gpio_line_config(int line, int style); +extern void gpio_line_config(int line, int direction); static inline int gpio_line_get(int line) { @@ -45,11 +37,12 @@ static inline int gpio_line_get(int line) static inline void gpio_line_set(int line, int value) { if (value == IXP2000_GPIO_HIGH) { - ixp_reg_write(IXP2000_GPIO_POSR, BIT(line)); - } else if (value == IXP2000_GPIO_LOW) - ixp_reg_write(IXP2000_GPIO_POCR, BIT(line)); + ixp2000_reg_write(IXP2000_GPIO_POSR, 1 << line); + } else if (value == IXP2000_GPIO_LOW) { + ixp2000_reg_write(IXP2000_GPIO_POCR, 1 << line); + } } #endif /* !__ASSEMBLY__ */ -#endif /* ASM_ARCH_IXP2000_GPIO_H_ */ +#endif /* ASM_ARCH_IXP2000_GPIO_H_ */ diff --git a/include/asm-arm/arch-ixp2000/io.h b/include/asm-arm/arch-ixp2000/io.h index 083462668e18..5e56b47446e0 100644 --- a/include/asm-arm/arch-ixp2000/io.h +++ b/include/asm-arm/arch-ixp2000/io.h @@ -27,8 +27,8 @@ * since that isn't available on the A? revisions we just keep doing * things manually. */ -#define alignb(addr) (void __iomem *)((unsigned long)addr ^ 3) -#define alignw(addr) (void __iomem *)((unsigned long)addr ^ 2) +#define alignb(addr) (void __iomem *)((unsigned long)(addr) ^ 3) +#define alignw(addr) (void __iomem *)((unsigned long)(addr) ^ 2) #define outb(v,p) __raw_writeb((v),alignb(___io(p))) #define outw(v,p) __raw_writew((v),alignw(___io(p))) @@ -48,6 +48,78 @@ #define insw(p,d,l) __raw_readsw(alignw(___io(p)),d,l) #define insl(p,d,l) __raw_readsl(___io(p),d,l) +#define __is_io_address(p) ((((unsigned long)(p)) & ~(IXP2000_PCI_IO_SIZE - 1)) == IXP2000_PCI_IO_VIRT_BASE) + +#define ioread8(p) \ + ({ \ + unsigned int __v; \ + \ + if (__is_io_address(p)) { \ + __v = __raw_readb(alignb(p)); \ + } else { \ + __v = __raw_readb(p); \ + } \ + \ + __v; \ + }) \ + +#define ioread16(p) \ + ({ \ + unsigned int __v; \ + \ + if (__is_io_address(p)) { \ + __v = __raw_readw(alignw(p)); \ + } else { \ + __v = le16_to_cpu(__raw_readw(p)); \ + } \ + \ + __v; \ + }) + +#define ioread32(p) \ + ({ \ + unsigned int __v; \ + \ + if (__is_io_address(p)) { \ + __v = __raw_readl(p); \ + } else { \ + __v = le32_to_cpu(__raw_readl(p)); \ + } \ + \ + __v; \ + }) + +#define iowrite8(v,p) \ + ({ \ + if (__is_io_address(p)) { \ + __raw_writeb((v), alignb(p)); \ + } else { \ + __raw_writeb((v), p); \ + } \ + }) + +#define iowrite16(v,p) \ + ({ \ + if (__is_io_address(p)) { \ + __raw_writew((v), alignw(p)); \ + } else { \ + __raw_writew(cpu_to_le16(v), p); \ + } \ + }) + +#define iowrite32(v,p) \ + ({ \ + if (__is_io_address(p)) { \ + __raw_writel((v), p); \ + } else { \ + __raw_writel(cpu_to_le32(v), p); \ + } \ + }) + +#define ioport_map(port, nr) ___io(port) + +#define ioport_unmap(addr) + #ifdef CONFIG_ARCH_IXDP2X01 /* diff --git a/include/asm-arm/arch-ixp2000/platform.h b/include/asm-arm/arch-ixp2000/platform.h index 901bba6d02b4..52ded516ea5c 100644 --- a/include/asm-arm/arch-ixp2000/platform.h +++ b/include/asm-arm/arch-ixp2000/platform.h @@ -138,30 +138,10 @@ struct ixp2000_flash_data { unsigned long (*bank_setup)(unsigned long); }; -/* - * GPIO helper functions - */ -#define GPIO_IN 0 -#define GPIO_OUT 1 - -extern void gpio_line_config(int line, int style); - -static inline int gpio_line_get(int line) -{ - return (((*IXP2000_GPIO_PLR) >> line) & 1); -} - -static inline void gpio_line_set(int line, int value) -{ - if (value) - ixp2000_reg_write(IXP2000_GPIO_POSR, (1 << line)); - else - ixp2000_reg_write(IXP2000_GPIO_POCR, (1 << line)); -} - struct ixp2000_i2c_pins { unsigned long sda_pin; unsigned long scl_pin; }; + #endif /* !__ASSEMBLY__ */ diff --git a/include/asm-arm/arch-ixp4xx/debug-macro.S b/include/asm-arm/arch-ixp4xx/debug-macro.S index 4499ae8e4b44..45a6c6cc29d5 100644 --- a/include/asm-arm/arch-ixp4xx/debug-macro.S +++ b/include/asm-arm/arch-ixp4xx/debug-macro.S @@ -14,6 +14,7 @@ mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? moveq \rx, #0xc8000000 + orrne \rx, \rx, #0x00b00000 movne \rx, #0xff000000 add \rx,\rx,#3 @ Uart regs are at off set of 3 if @ byte writes used - Big Endian. diff --git a/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h b/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h index 8eeb1db6309d..004696a95bdb 100644 --- a/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h +++ b/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h @@ -69,6 +69,16 @@ #define IXP4XX_PERIPHERAL_BASE_VIRT (0xFFBF2000) #define IXP4XX_PERIPHERAL_REGION_SIZE (0x0000C000) +/* + * Debug UART + * + * This is basically a remap of UART1 into a region that is section + * aligned so that it * can be used with the low-level debug code. + */ +#define IXP4XX_DEBUG_UART_BASE_PHYS (0xC8000000) +#define IXP4XX_DEBUG_UART_BASE_VIRT (0xffb00000) +#define IXP4XX_DEBUG_UART_REGION_SIZE (0x00001000) + #define IXP4XX_EXP_CS0_OFFSET 0x00 #define IXP4XX_EXP_CS1_OFFSET 0x04 #define IXP4XX_EXP_CS2_OFFSET 0x08 diff --git a/include/asm-arm/io.h b/include/asm-arm/io.h index 08a46302d265..cc4b5f5dbfcf 100644 --- a/include/asm-arm/io.h +++ b/include/asm-arm/io.h @@ -275,6 +275,7 @@ extern void __iounmap(void __iomem *addr); /* * io{read,write}{8,16,32} macros */ +#ifndef ioread8 #define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; }) #define ioread16(p) ({ unsigned int __v = le16_to_cpu(__raw_readw(p)); __v; }) #define ioread32(p) ({ unsigned int __v = le32_to_cpu(__raw_readl(p)); __v; }) @@ -293,6 +294,7 @@ extern void __iounmap(void __iomem *addr); extern void __iomem *ioport_map(unsigned long port, unsigned int nr); extern void ioport_unmap(void __iomem *addr); +#endif struct pci_dev; diff --git a/include/asm-arm/thread_info.h b/include/asm-arm/thread_info.h index 66c585c50cf9..8252a4cd860f 100644 --- a/include/asm-arm/thread_info.h +++ b/include/asm-arm/thread_info.h @@ -49,7 +49,7 @@ struct cpu_context_save { */ struct thread_info { unsigned long flags; /* low level flags */ - __s32 preempt_count; /* 0 => preemptable, <0 => bug */ + int preempt_count; /* 0 => preemptable, <0 => bug */ mm_segment_t addr_limit; /* address limit */ struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ diff --git a/include/asm-arm26/thread_info.h b/include/asm-arm26/thread_info.h index 50f41b50268a..aff3e5699c64 100644 --- a/include/asm-arm26/thread_info.h +++ b/include/asm-arm26/thread_info.h @@ -44,7 +44,7 @@ struct cpu_context_save { */ struct thread_info { unsigned long flags; /* low level flags */ - __s32 preempt_count; /* 0 => preemptable, <0 => bug */ + int preempt_count; /* 0 => preemptable, <0 => bug */ mm_segment_t addr_limit; /* address limit */ struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ diff --git a/include/asm-cris/thread_info.h b/include/asm-cris/thread_info.h index 53193feb0826..5ba4b7865cc5 100644 --- a/include/asm-cris/thread_info.h +++ b/include/asm-cris/thread_info.h @@ -31,7 +31,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead diff --git a/include/asm-frv/thread_info.h b/include/asm-frv/thread_info.h index b80a97f50af6..c8cba7836f0d 100644 --- a/include/asm-frv/thread_info.h +++ b/include/asm-frv/thread_info.h @@ -33,7 +33,7 @@ struct thread_info { unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index 3b709b84934f..9044aeb37828 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -29,7 +29,7 @@ do { \ #define DEFINE_PER_CPU(type, name) \ __typeof__(type) per_cpu__##name -#define per_cpu(var, cpu) (*((void)cpu, &per_cpu__##var)) +#define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var #endif /* SMP */ diff --git a/include/asm-generic/topology.h b/include/asm-generic/topology.h index ec96e8b0f190..5d9d70cd17fc 100644 --- a/include/asm-generic/topology.h +++ b/include/asm-generic/topology.h @@ -41,8 +41,15 @@ #ifndef node_to_first_cpu #define node_to_first_cpu(node) (0) #endif +#ifndef pcibus_to_node +#define pcibus_to_node(node) (-1) +#endif + #ifndef pcibus_to_cpumask -#define pcibus_to_cpumask(bus) (cpu_online_map) +#define pcibus_to_cpumask(bus) (pcibus_to_node(bus) == -1 ? \ + CPU_MASK_ALL : \ + node_to_cpumask(pcibus_to_node(bus)) \ + ) #endif #endif /* _ASM_GENERIC_TOPOLOGY_H */ diff --git a/include/asm-h8300/thread_info.h b/include/asm-h8300/thread_info.h index b07c9344776f..bfcc755c3bb1 100644 --- a/include/asm-h8300/thread_info.h +++ b/include/asm-h8300/thread_info.h @@ -23,7 +23,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ int cpu; /* cpu we're on */ - int preempt_count; /* 0 => preemptable, <0 => BUG*/ + int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; }; diff --git a/include/asm-i386/checksum.h b/include/asm-i386/checksum.h index 641342002bcd..f949e44c2a35 100644 --- a/include/asm-i386/checksum.h +++ b/include/asm-i386/checksum.h @@ -3,6 +3,8 @@ #include +#include + /* * computes the checksum of a memory block at buff, length len, * and adds in "sum" (32-bit) diff --git a/include/asm-i386/genapic.h b/include/asm-i386/genapic.h index fc813b2e8274..b3783a32abee 100644 --- a/include/asm-i386/genapic.h +++ b/include/asm-i386/genapic.h @@ -78,7 +78,6 @@ struct genapic { .int_delivery_mode = INT_DELIVERY_MODE, \ .int_dest_mode = INT_DEST_MODE, \ .no_balance_irq = NO_BALANCE_IRQ, \ - .no_ioapic_check = NO_IOAPIC_CHECK, \ .ESR_DISABLE = esr_disable, \ .apic_destination_logical = APIC_DEST_LOGICAL, \ APICFUNC(apic_id_registered), \ diff --git a/include/asm-i386/kprobes.h b/include/asm-i386/kprobes.h index 4092f68d123a..8b6d3a90cd78 100644 --- a/include/asm-i386/kprobes.h +++ b/include/asm-i386/kprobes.h @@ -39,6 +39,9 @@ typedef u8 kprobe_opcode_t; : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry +#define ARCH_SUPPORTS_KRETPROBES + +void kretprobe_trampoline(void); /* Architecture specific copy of original instruction*/ struct arch_specific_insn { diff --git a/include/asm-i386/mach-bigsmp/mach_apic.h b/include/asm-i386/mach-bigsmp/mach_apic.h index 2339868270ef..ba936d4daedb 100644 --- a/include/asm-i386/mach-bigsmp/mach_apic.h +++ b/include/asm-i386/mach-bigsmp/mach_apic.h @@ -14,8 +14,6 @@ #define NO_BALANCE_IRQ (1) #define esr_disable (1) -#define NO_IOAPIC_CHECK (0) - static inline int apic_id_registered(void) { return (1); diff --git a/include/asm-i386/mach-default/mach_apic.h b/include/asm-i386/mach-default/mach_apic.h index 627f1cd084ba..3ef6292db780 100644 --- a/include/asm-i386/mach-default/mach_apic.h +++ b/include/asm-i386/mach-default/mach_apic.h @@ -19,8 +19,6 @@ static inline cpumask_t target_cpus(void) #define NO_BALANCE_IRQ (0) #define esr_disable (0) -#define NO_IOAPIC_CHECK (0) - #define INT_DELIVERY_MODE dest_LowestPrio #define INT_DEST_MODE 1 /* logical delivery broadcast to all procs */ diff --git a/include/asm-i386/mach-es7000/mach_apic.h b/include/asm-i386/mach-es7000/mach_apic.h index ceab2c464b13..b5f3f0d0b2bc 100644 --- a/include/asm-i386/mach-es7000/mach_apic.h +++ b/include/asm-i386/mach-es7000/mach_apic.h @@ -38,8 +38,6 @@ static inline cpumask_t target_cpus(void) #define WAKE_SECONDARY_VIA_INIT #endif -#define NO_IOAPIC_CHECK (1) - static inline unsigned long check_apicid_used(physid_mask_t bitmap, int apicid) { return 0; diff --git a/include/asm-i386/mach-generic/mach_apic.h b/include/asm-i386/mach-generic/mach_apic.h index ab36d02ebede..b13767a4e934 100644 --- a/include/asm-i386/mach-generic/mach_apic.h +++ b/include/asm-i386/mach-generic/mach_apic.h @@ -5,7 +5,6 @@ #define esr_disable (genapic->ESR_DISABLE) #define NO_BALANCE_IRQ (genapic->no_balance_irq) -#define NO_IOAPIC_CHECK (genapic->no_ioapic_check) #define INT_DELIVERY_MODE (genapic->int_delivery_mode) #define INT_DEST_MODE (genapic->int_dest_mode) #undef APIC_DEST_LOGICAL diff --git a/include/asm-i386/mach-numaq/mach_apic.h b/include/asm-i386/mach-numaq/mach_apic.h index e1a04494764a..9d158095da82 100644 --- a/include/asm-i386/mach-numaq/mach_apic.h +++ b/include/asm-i386/mach-numaq/mach_apic.h @@ -17,8 +17,6 @@ static inline cpumask_t target_cpus(void) #define NO_BALANCE_IRQ (1) #define esr_disable (1) -#define NO_IOAPIC_CHECK (0) - #define INT_DELIVERY_MODE dest_LowestPrio #define INT_DEST_MODE 0 /* physical delivery on LOCAL quad */ diff --git a/include/asm-i386/mach-summit/mach_apic.h b/include/asm-i386/mach-summit/mach_apic.h index 74e9cbc8c01b..3d6d12937e1f 100644 --- a/include/asm-i386/mach-summit/mach_apic.h +++ b/include/asm-i386/mach-summit/mach_apic.h @@ -7,8 +7,6 @@ #define esr_disable (1) #define NO_BALANCE_IRQ (0) -#define NO_IOAPIC_CHECK (1) /* Don't check I/O APIC ID for xAPIC */ - /* In clustered mode, the high nibble of APIC ID is a cluster number. * The low nibble is a 4-bit bitmap. */ #define XAPIC_DEST_CPUS_SHIFT 4 diff --git a/include/asm-i386/mach-visws/mach_apic.h b/include/asm-i386/mach-visws/mach_apic.h index 4e6cdfb8b091..de438c7147a8 100644 --- a/include/asm-i386/mach-visws/mach_apic.h +++ b/include/asm-i386/mach-visws/mach_apic.h @@ -9,8 +9,6 @@ #define no_balance_irq (0) #define esr_disable (0) -#define NO_IOAPIC_CHECK (0) - #define INT_DELIVERY_MODE dest_LowestPrio #define INT_DEST_MODE 1 /* logical delivery broadcast to all procs */ diff --git a/include/asm-i386/mmzone.h b/include/asm-i386/mmzone.h index 13830ae67cac..33ce5d37e894 100644 --- a/include/asm-i386/mmzone.h +++ b/include/asm-i386/mmzone.h @@ -8,7 +8,9 @@ #include -#ifdef CONFIG_DISCONTIGMEM +#if CONFIG_NUMA +extern struct pglist_data *node_data[]; +#define NODE_DATA(nid) (node_data[nid]) #ifdef CONFIG_NUMA #ifdef CONFIG_X86_NUMAQ @@ -21,8 +23,28 @@ #define get_zholes_size(n) (0) #endif /* CONFIG_NUMA */ -extern struct pglist_data *node_data[]; -#define NODE_DATA(nid) (node_data[nid]) +extern int get_memcfg_numa_flat(void ); +/* + * This allows any one NUMA architecture to be compiled + * for, and still fall back to the flat function if it + * fails. + */ +static inline void get_memcfg_numa(void) +{ +#ifdef CONFIG_X86_NUMAQ + if (get_memcfg_numaq()) + return; +#elif CONFIG_ACPI_SRAT + if (get_memcfg_from_srat()) + return; +#endif + + get_memcfg_numa_flat(); +} + +#endif /* CONFIG_NUMA */ + +#ifdef CONFIG_DISCONTIGMEM /* * generic node memory support, the following assumptions apply: @@ -48,26 +70,6 @@ static inline int pfn_to_nid(unsigned long pfn) #endif } -/* - * Following are macros that are specific to this numa platform. - */ -#define reserve_bootmem(addr, size) \ - reserve_bootmem_node(NODE_DATA(0), (addr), (size)) -#define alloc_bootmem(x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) -#define alloc_bootmem_low(x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, 0) -#define alloc_bootmem_pages(x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) -#define alloc_bootmem_low_pages(x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, 0) -#define alloc_bootmem_node(ignore, x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) -#define alloc_bootmem_pages_node(ignore, x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) -#define alloc_bootmem_low_pages_node(ignore, x) \ - __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, 0) - #define node_localnr(pfn, nid) ((pfn) - node_data[nid]->node_start_pfn) /* @@ -79,7 +81,6 @@ static inline int pfn_to_nid(unsigned long pfn) */ #define kvaddr_to_nid(kaddr) pfn_to_nid(__pa(kaddr) >> PAGE_SHIFT) -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_end_pfn(nid) \ ({ \ @@ -100,7 +101,7 @@ static inline int pfn_to_nid(unsigned long pfn) ({ \ unsigned long __pfn = pfn; \ int __node = pfn_to_nid(__pfn); \ - &node_mem_map(__node)[node_localnr(__pfn,__node)]; \ + &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ }) #define page_to_pfn(pg) \ @@ -122,26 +123,34 @@ static inline int pfn_valid(int pfn) return (pfn < node_end_pfn(nid)); return 0; } -#endif - -extern int get_memcfg_numa_flat(void ); -/* - * This allows any one NUMA architecture to be compiled - * for, and still fall back to the flat function if it - * fails. - */ -static inline void get_memcfg_numa(void) -{ -#ifdef CONFIG_X86_NUMAQ - if (get_memcfg_numaq()) - return; -#elif CONFIG_ACPI_SRAT - if (get_memcfg_from_srat()) - return; -#endif - - get_memcfg_numa_flat(); -} +#endif /* CONFIG_X86_NUMAQ */ #endif /* CONFIG_DISCONTIGMEM */ + +#ifdef CONFIG_NEED_MULTIPLE_NODES + +/* + * Following are macros that are specific to this numa platform. + */ +#define reserve_bootmem(addr, size) \ + reserve_bootmem_node(NODE_DATA(0), (addr), (size)) +#define alloc_bootmem(x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_low(x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, 0) +#define alloc_bootmem_pages(x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_low_pages(x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, 0) +#define alloc_bootmem_node(ignore, x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_pages_node(ignore, x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_low_pages_node(ignore, x) \ + __alloc_bootmem_node(NODE_DATA(0), (x), PAGE_SIZE, 0) + +#endif /* CONFIG_NEED_MULTIPLE_NODES */ + +extern int early_pfn_to_nid(unsigned long pfn); + #endif /* _ASM_MMZONE_H_ */ diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h index 41400d342d44..dea8f8e6d86e 100644 --- a/include/asm-i386/page.h +++ b/include/asm-i386/page.h @@ -120,6 +120,8 @@ static __inline__ int get_order(unsigned long size) extern int sysctl_legacy_va_layout; +extern int page_is_ram(unsigned long pagenr); + #endif /* __ASSEMBLY__ */ #ifdef __ASSEMBLY__ @@ -135,11 +137,11 @@ extern int sysctl_legacy_va_layout; #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_FLATMEM #define pfn_to_page(pfn) (mem_map + (pfn)) #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) -#endif /* !CONFIG_DISCONTIGMEM */ +#endif /* CONFIG_FLATMEM */ #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) diff --git a/include/asm-i386/param.h b/include/asm-i386/param.h index b6440526e42a..fa02e67ea86b 100644 --- a/include/asm-i386/param.h +++ b/include/asm-i386/param.h @@ -1,8 +1,10 @@ +#include + #ifndef _ASMi386_PARAM_H #define _ASMi386_PARAM_H #ifdef __KERNEL__ -# define HZ 1000 /* Internal kernel timer frequency */ +# define HZ CONFIG_HZ /* Internal kernel timer frequency */ # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ #endif diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index e9efe148fdf7..77c6497f416e 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -398,9 +398,9 @@ extern void noexec_setup(const char *str); #endif /* !__ASSEMBLY__ */ -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_FLATMEM #define kern_addr_valid(addr) (1) -#endif /* !CONFIG_DISCONTIGMEM */ +#endif /* CONFIG_FLATMEM */ #define io_remap_page_range(vma, vaddr, paddr, size, prot) \ remap_pfn_range(vma, vaddr, (paddr) >> PAGE_SHIFT, size, prot) diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 359bb0151742..c76c50e96225 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -501,12 +501,16 @@ static inline void load_esp0(struct tss_struct *tss, struct thread_struct *threa } while (0) /* - * This special macro can be used to load a debugging register + * These special macros can be used to get or set a debugging register */ -#define loaddebug(thread,register) \ - __asm__("movl %0,%%db" #register \ - : /* no output */ \ - :"r" ((thread)->debugreg[register])) +#define get_debugreg(var, register) \ + __asm__("movl %%db" #register ", %0" \ + :"=r" (var)) +#define set_debugreg(value, register) \ + __asm__("movl %0,%%db" #register \ + : /* no output */ \ + :"r" (value)) + /* Forward declaration, a strange C thing */ struct task_struct; diff --git a/include/asm-i386/ptrace.h b/include/asm-i386/ptrace.h index 8618914b3521..eef9f93870d4 100644 --- a/include/asm-i386/ptrace.h +++ b/include/asm-i386/ptrace.h @@ -57,7 +57,8 @@ struct pt_regs { #ifdef __KERNEL__ struct task_struct; extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code); -#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs)) +#define user_mode(regs) (3 & (regs)->xcs) +#define user_mode_vm(regs) ((VM_MASK & (regs)->eflags) || user_mode(regs)) #define instruction_pointer(regs) ((regs)->eip) #if defined(CONFIG_SMP) && defined(CONFIG_FRAME_POINTER) extern unsigned long profile_pc(struct pt_regs *regs); diff --git a/include/asm-i386/sparsemem.h b/include/asm-i386/sparsemem.h new file mode 100644 index 000000000000..cfeed990585f --- /dev/null +++ b/include/asm-i386/sparsemem.h @@ -0,0 +1,31 @@ +#ifndef _I386_SPARSEMEM_H +#define _I386_SPARSEMEM_H +#ifdef CONFIG_SPARSEMEM + +/* + * generic non-linear memory support: + * + * 1) we will not split memory into more chunks than will fit into the + * flags field of the struct page + */ + +/* + * SECTION_SIZE_BITS 2^N: how big each section will be + * MAX_PHYSADDR_BITS 2^N: how much physical address space we have + * MAX_PHYSMEM_BITS 2^N: how much memory we can have in that space + */ +#ifdef CONFIG_X86_PAE +#define SECTION_SIZE_BITS 30 +#define MAX_PHYSADDR_BITS 36 +#define MAX_PHYSMEM_BITS 36 +#else +#define SECTION_SIZE_BITS 26 +#define MAX_PHYSADDR_BITS 32 +#define MAX_PHYSMEM_BITS 32 +#endif + +/* XXX: FIXME -- wli */ +#define kern_addr_valid(kaddr) (0) + +#endif /* CONFIG_SPARSEMEM */ +#endif /* _I386_SPARSEMEM_H */ diff --git a/include/asm-i386/string.h b/include/asm-i386/string.h index 6a78ac58c194..02c8f5d22065 100644 --- a/include/asm-i386/string.h +++ b/include/asm-i386/string.h @@ -116,7 +116,8 @@ __asm__ __volatile__( "orb $1,%%al\n" "3:" :"=a" (__res), "=&S" (d0), "=&D" (d1) - :"1" (cs),"2" (ct)); + :"1" (cs),"2" (ct) + :"memory"); return __res; } @@ -138,8 +139,9 @@ __asm__ __volatile__( "3:\tsbbl %%eax,%%eax\n\t" "orb $1,%%al\n" "4:" - :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2) - :"1" (cs),"2" (ct),"3" (count)); + :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2) + :"1" (cs),"2" (ct),"3" (count) + :"memory"); return __res; } @@ -158,7 +160,9 @@ __asm__ __volatile__( "movl $1,%1\n" "2:\tmovl %1,%0\n\t" "decl %0" - :"=a" (__res), "=&S" (d0) : "1" (s),"0" (c)); + :"=a" (__res), "=&S" (d0) + :"1" (s),"0" (c) + :"memory"); return __res; } @@ -175,7 +179,9 @@ __asm__ __volatile__( "leal -1(%%esi),%0\n" "2:\ttestb %%al,%%al\n\t" "jne 1b" - :"=g" (__res), "=&S" (d0), "=&a" (d1) :"0" (0),"1" (s),"2" (c)); + :"=g" (__res), "=&S" (d0), "=&a" (d1) + :"0" (0),"1" (s),"2" (c) + :"memory"); return __res; } @@ -189,7 +195,9 @@ __asm__ __volatile__( "scasb\n\t" "notl %0\n\t" "decl %0" - :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffffu)); + :"=c" (__res), "=&D" (d0) + :"1" (s),"a" (0), "0" (0xffffffffu) + :"memory"); return __res; } @@ -333,7 +341,9 @@ __asm__ __volatile__( "je 1f\n\t" "movl $1,%0\n" "1:\tdecl %0" - :"=D" (__res), "=&c" (d0) : "a" (c),"0" (cs),"1" (count)); + :"=D" (__res), "=&c" (d0) + :"a" (c),"0" (cs),"1" (count) + :"memory"); return __res; } @@ -369,7 +379,7 @@ __asm__ __volatile__( "je 2f\n\t" "stosb\n" "2:" - : "=&c" (d0), "=&D" (d1) + :"=&c" (d0), "=&D" (d1) :"a" (c), "q" (count), "0" (count/4), "1" ((long) s) :"memory"); return (s); @@ -392,7 +402,8 @@ __asm__ __volatile__( "jne 1b\n" "3:\tsubl %2,%0" :"=a" (__res), "=&d" (d0) - :"c" (s),"1" (count)); + :"c" (s),"1" (count) + :"memory"); return __res; } /* end of additional stuff */ @@ -473,7 +484,8 @@ static inline void * memscan(void * addr, int c, size_t size) "dec %%edi\n" "1:" : "=D" (addr), "=c" (size) - : "0" (addr), "1" (size), "a" (c)); + : "0" (addr), "1" (size), "a" (c) + : "memory"); return addr; } diff --git a/include/asm-i386/thread_info.h b/include/asm-i386/thread_info.h index 2cd57271801d..95add81237ea 100644 --- a/include/asm-i386/thread_info.h +++ b/include/asm-i386/thread_info.h @@ -31,7 +31,7 @@ struct thread_info { unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: diff --git a/include/asm-i386/timer.h b/include/asm-i386/timer.h index c34709849839..dcf1e07db08a 100644 --- a/include/asm-i386/timer.h +++ b/include/asm-i386/timer.h @@ -22,6 +22,7 @@ struct timer_opts { unsigned long (*get_offset)(void); unsigned long long (*monotonic_clock)(void); void (*delay)(unsigned long); + unsigned long (*read_timer)(void); }; struct init_timer_opts { @@ -52,6 +53,7 @@ extern struct init_timer_opts timer_cyclone_init; #endif extern unsigned long calibrate_tsc(void); +extern unsigned long read_timer_tsc(void); extern void init_cpu_khz(void); extern int recalibrate_cpu_khz(void); #ifdef CONFIG_HPET_TIMER diff --git a/include/asm-i386/timex.h b/include/asm-i386/timex.h index b41e484c3445..292b5a68f627 100644 --- a/include/asm-i386/timex.h +++ b/include/asm-i386/timex.h @@ -47,6 +47,9 @@ static inline cycles_t get_cycles (void) return ret; } -extern unsigned long cpu_khz; +extern unsigned int cpu_khz; + +extern int read_current_timer(unsigned long *timer_value); +#define ARCH_HAS_READ_CURRENT_TIMER 1 #endif diff --git a/include/asm-i386/topology.h b/include/asm-i386/topology.h index 98f9e6850cba..6d0f67507b21 100644 --- a/include/asm-i386/topology.h +++ b/include/asm-i386/topology.h @@ -60,12 +60,8 @@ static inline int node_to_first_cpu(int node) return first_cpu(mask); } -/* Returns the number of the node containing PCI bus number 'busnr' */ -static inline cpumask_t __pcibus_to_cpumask(int busnr) -{ - return node_to_cpumask(mp_bus_id_to_node[busnr]); -} -#define pcibus_to_cpumask(bus) __pcibus_to_cpumask(bus->number) +#define pcibus_to_node(bus) mp_bus_id_to_node[(bus)->number] +#define pcibus_to_cpumask(bus) node_to_cpumask(pcibus_to_node(bus)) /* sched_domains SD_NODE_INIT for NUMAQ machines */ #define SD_NODE_INIT (struct sched_domain) { \ diff --git a/include/asm-ia64/break.h b/include/asm-ia64/break.h index 97c7b2d79600..8167828edc4b 100644 --- a/include/asm-ia64/break.h +++ b/include/asm-ia64/break.h @@ -12,6 +12,8 @@ * OS-specific debug break numbers: */ #define __IA64_BREAK_KDB 0x80100 +#define __IA64_BREAK_KPROBE 0x80200 +#define __IA64_BREAK_JPROBE 0x80300 /* * OS-specific break numbers: diff --git a/include/asm-ia64/compat.h b/include/asm-ia64/compat.h index cc0ff0a4bdd0..0c05e5bad8a0 100644 --- a/include/asm-ia64/compat.h +++ b/include/asm-ia64/compat.h @@ -27,6 +27,7 @@ typedef u16 compat_ipc_pid_t; typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; +typedef s32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; diff --git a/include/asm-ia64/fcntl.h b/include/asm-ia64/fcntl.h index d193981bb1d8..c9f8d835d0cc 100644 --- a/include/asm-ia64/fcntl.h +++ b/include/asm-ia64/fcntl.h @@ -81,4 +81,6 @@ struct flock { #define F_LINUX_SPECIFIC_BASE 1024 +#define force_o_largefile() ( ! (current->personality & PER_LINUX32) ) + #endif /* _ASM_IA64_FCNTL_H */ diff --git a/include/asm-ia64/kdebug.h b/include/asm-ia64/kdebug.h new file mode 100644 index 000000000000..4d376e1663f7 --- /dev/null +++ b/include/asm-ia64/kdebug.h @@ -0,0 +1,61 @@ +#ifndef _IA64_KDEBUG_H +#define _IA64_KDEBUG_H 1 +/* + * include/asm-ia64/kdebug.h + * + * 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. + * + * Copyright (C) Intel Corporation, 2005 + * + * 2005-Apr Rusty Lynch and Anil S Keshavamurthy + * adopted from + * include/asm-x86_64/kdebug.h + */ +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + long err; + int trapnr; + int signr; +}; + +int register_die_notifier(struct notifier_block *nb); +extern struct notifier_block *ia64die_chain; + +enum die_val { + DIE_BREAK = 1, + DIE_SS, + DIE_PAGE_FAULT, +}; + +static inline int notify_die(enum die_val val, char *str, struct pt_regs *regs, + long err, int trap, int sig) +{ + struct die_args args = { + .regs = regs, + .str = str, + .err = err, + .trapnr = trap, + .signr = sig + }; + + return notifier_call_chain(&ia64die_chain, val, &args); +} + +#endif diff --git a/include/asm-ia64/kprobes.h b/include/asm-ia64/kprobes.h new file mode 100644 index 000000000000..7b700035e36d --- /dev/null +++ b/include/asm-ia64/kprobes.h @@ -0,0 +1,116 @@ +#ifndef _ASM_KPROBES_H +#define _ASM_KPROBES_H +/* + * Kernel Probes (KProbes) + * include/asm-ia64/kprobes.h + * + * 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. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * Copyright (C) Intel Corporation, 2005 + * + * 2005-Apr Rusty Lynch and Anil S Keshavamurthy + * adapted from i386 + */ +#include +#include +#include + +#define BREAK_INST (long)(__IA64_BREAK_KPROBE << 6) + +typedef union cmp_inst { + struct { + unsigned long long qp : 6; + unsigned long long p1 : 6; + unsigned long long c : 1; + unsigned long long r2 : 7; + unsigned long long r3 : 7; + unsigned long long p2 : 6; + unsigned long long ta : 1; + unsigned long long x2 : 2; + unsigned long long tb : 1; + unsigned long long opcode : 4; + unsigned long long reserved : 23; + }f; + unsigned long long l; +} cmp_inst_t; + +struct kprobe; + +typedef struct _bundle { + struct { + unsigned long long template : 5; + unsigned long long slot0 : 41; + unsigned long long slot1_p0 : 64-46; + } quad0; + struct { + unsigned long long slot1_p1 : 41 - (64-46); + unsigned long long slot2 : 41; + } quad1; +} __attribute__((__aligned__(16))) bundle_t; + +#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry + +#define SLOT0_OPCODE_SHIFT (37) +#define SLOT1_p1_OPCODE_SHIFT (37 - (64-46)) +#define SLOT2_OPCODE_SHIFT (37) + +#define INDIRECT_CALL_OPCODE (1) +#define IP_RELATIVE_CALL_OPCODE (5) +#define IP_RELATIVE_BRANCH_OPCODE (4) +#define IP_RELATIVE_PREDICT_OPCODE (7) +#define LONG_BRANCH_OPCODE (0xC) +#define LONG_CALL_OPCODE (0xD) + +typedef struct kprobe_opcode { + bundle_t bundle; +} kprobe_opcode_t; + +struct fnptr { + unsigned long ip; + unsigned long gp; +}; + +/* Architecture specific copy of original instruction*/ +struct arch_specific_insn { + /* copy of the instruction to be emulated */ + kprobe_opcode_t insn; + #define INST_FLAG_FIX_RELATIVE_IP_ADDR 1 + #define INST_FLAG_FIX_BRANCH_REG 2 + unsigned long inst_flag; + unsigned short target_br_reg; +}; + +/* ia64 does not need this */ +static inline void jprobe_return(void) +{ +} + +/* ia64 does not need this */ +static inline void arch_copy_kprobe(struct kprobe *p) +{ +} + +#ifdef CONFIG_KPROBES +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); +#else /* !CONFIG_KPROBES */ +static inline int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + return 0; +} +#endif +#endif /* _ASM_KPROBES_H */ diff --git a/include/asm-ia64/mmzone.h b/include/asm-ia64/mmzone.h index 83ca4043fc11..d32f51e3d6c2 100644 --- a/include/asm-ia64/mmzone.h +++ b/include/asm-ia64/mmzone.h @@ -15,6 +15,8 @@ #include #include +#ifdef CONFIG_DISCONTIGMEM + static inline int pfn_to_nid(unsigned long pfn) { #ifdef CONFIG_NUMA @@ -29,8 +31,6 @@ static inline int pfn_to_nid(unsigned long pfn) #endif } -#ifdef CONFIG_DISCONTIGMEM - #ifdef CONFIG_IA64_DIG /* DIG systems are small */ # define MAX_PHYSNODE_ID 8 # define NR_NODE_MEMBLKS (MAX_NUMNODES * 8) diff --git a/include/asm-ia64/param.h b/include/asm-ia64/param.h index 6c6b679b7a9e..5e1e0d2d7baf 100644 --- a/include/asm-ia64/param.h +++ b/include/asm-ia64/param.h @@ -27,7 +27,7 @@ */ # define HZ 32 # else -# define HZ 1024 +# define HZ CONFIG_HZ # endif # define USER_HZ HZ # define CLOCKS_PER_SEC HZ /* frequency at which times() counts */ diff --git a/include/asm-ia64/percpu.h b/include/asm-ia64/percpu.h index 1e87f19dad56..2b14dee29ce7 100644 --- a/include/asm-ia64/percpu.h +++ b/include/asm-ia64/percpu.h @@ -50,7 +50,7 @@ extern void *per_cpu_init(void); #else /* ! SMP */ -#define per_cpu(var, cpu) (*((void)cpu, &per_cpu__##var)) +#define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var #define per_cpu_init() (__phys_per_cpu_start) diff --git a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h index 8d5b7e77028c..7dc8951708a3 100644 --- a/include/asm-ia64/thread_info.h +++ b/include/asm-ia64/thread_info.h @@ -25,7 +25,7 @@ struct thread_info { __u32 flags; /* thread_info flags (see TIF_*) */ __u32 cpu; /* current CPU */ mm_segment_t addr_limit; /* user-level address space limit */ - __s32 preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ + int preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ struct restart_block restart_block; struct { int signo; diff --git a/include/asm-m32r/mmzone.h b/include/asm-m32r/mmzone.h index ebf0228fec42..d58878ec899e 100644 --- a/include/asm-m32r/mmzone.h +++ b/include/asm-m32r/mmzone.h @@ -14,7 +14,6 @@ extern struct pglist_data *node_data[]; #define NODE_DATA(nid) (node_data[nid]) #define node_localnr(pfn, nid) ((pfn) - NODE_DATA(nid)->node_start_pfn) -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_end_pfn(nid) \ ({ \ @@ -32,7 +31,7 @@ extern struct pglist_data *node_data[]; ({ \ unsigned long __pfn = pfn; \ int __node = pfn_to_nid(__pfn); \ - &node_mem_map(__node)[node_localnr(__pfn,__node)]; \ + &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ }) #define page_to_pfn(pg) \ diff --git a/include/asm-m32r/thread_info.h b/include/asm-m32r/thread_info.h index 9f3a0fcf6e2b..7a6be7727a92 100644 --- a/include/asm-m32r/thread_info.h +++ b/include/asm-m32r/thread_info.h @@ -28,7 +28,7 @@ struct thread_info { unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thread diff --git a/include/asm-m32r/topology.h b/include/asm-m32r/topology.h index 299a89d91bde..d607eb32bd7e 100644 --- a/include/asm-m32r/topology.h +++ b/include/asm-m32r/topology.h @@ -1,48 +1,6 @@ -/* - * linux/include/asm-generic/topology.h - * - * Written by: Matthew Dobson, IBM Corporation - * - * Copyright (C) 2002, IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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. - * - * Send feedback to - */ #ifndef _ASM_M32R_TOPOLOGY_H #define _ASM_M32R_TOPOLOGY_H -/* Other architectures wishing to use this simple topology API should fill - in the below functions as appropriate in their own file. */ - -#define cpu_to_node(cpu) (0) - -#ifndef parent_node -#define parent_node(node) (0) -#endif -#ifndef node_to_cpumask -#define node_to_cpumask(node) (cpu_online_map) -#endif -#ifndef node_to_first_cpu -#define node_to_first_cpu(node) (0) -#endif -#ifndef pcibus_to_cpumask -#define pcibus_to_cpumask(bus) (cpu_online_map) -#endif +#include #endif /* _ASM_M32R_TOPOLOGY_H */ diff --git a/include/asm-m68k/thread_info.h b/include/asm-m68k/thread_info.h index 5f58939c59db..2aed24f6fd2e 100644 --- a/include/asm-m68k/thread_info.h +++ b/include/asm-m68k/thread_info.h @@ -8,7 +8,7 @@ struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ __u32 cpu; /* should always be 0 on m68k */ struct restart_block restart_block; diff --git a/include/asm-m68knommu/thread_info.h b/include/asm-m68knommu/thread_info.h index c8153b7c1f5c..7b9a3fa3af5d 100644 --- a/include/asm-m68knommu/thread_info.h +++ b/include/asm-m68knommu/thread_info.h @@ -36,7 +36,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ int cpu; /* cpu we're on */ - int preempt_count; /* 0 => preemptable, <0 => BUG*/ + int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; }; diff --git a/include/asm-mips/compat.h b/include/asm-mips/compat.h index dce92079e7fc..d78002afb1e1 100644 --- a/include/asm-mips/compat.h +++ b/include/asm-mips/compat.h @@ -29,6 +29,7 @@ typedef s32 compat_caddr_t; typedef struct { s32 val[2]; } compat_fsid_t; +typedef s32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; diff --git a/include/asm-mips/thread_info.h b/include/asm-mips/thread_info.h index 768900305e2f..42fcd6f2c206 100644 --- a/include/asm-mips/thread_info.h +++ b/include/asm-mips/thread_info.h @@ -27,7 +27,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead diff --git a/include/asm-parisc/compat.h b/include/asm-parisc/compat.h index ca0eac647a05..7630d1ad2391 100644 --- a/include/asm-parisc/compat.h +++ b/include/asm-parisc/compat.h @@ -24,7 +24,7 @@ typedef u16 compat_nlink_t; typedef u16 compat_ipc_pid_t; typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; -typedef u32 compat_timer_t; +typedef s32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; diff --git a/include/asm-parisc/mmzone.h b/include/asm-parisc/mmzone.h index 928bf50c4693..595d3dce120a 100644 --- a/include/asm-parisc/mmzone.h +++ b/include/asm-parisc/mmzone.h @@ -19,7 +19,6 @@ extern struct node_map_data node_data[]; */ #define kvaddr_to_nid(kaddr) pfn_to_nid(__pa(kaddr) >> PAGE_SHIFT) -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_end_pfn(nid) \ ({ \ @@ -38,7 +37,7 @@ extern struct node_map_data node_data[]; ({ \ unsigned long __pfn = (pfn); \ int __node = pfn_to_nid(__pfn); \ - &node_mem_map(__node)[node_localnr(__pfn,__node)]; \ + &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ }) #define page_to_pfn(pg) \ diff --git a/include/asm-parisc/thread_info.h b/include/asm-parisc/thread_info.h index fe9b7f8ae4c6..57bbb76cb6c1 100644 --- a/include/asm-parisc/thread_info.h +++ b/include/asm-parisc/thread_info.h @@ -12,7 +12,7 @@ struct thread_info { unsigned long flags; /* thread_info flags (see TIF_*) */ mm_segment_t addr_limit; /* user-level address space limit */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ + int preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ struct restart_block restart_block; }; diff --git a/include/asm-ppc/thread_info.h b/include/asm-ppc/thread_info.h index e3b5284a6f91..27903db42efc 100644 --- a/include/asm-ppc/thread_info.h +++ b/include/asm-ppc/thread_info.h @@ -20,7 +20,8 @@ struct thread_info { unsigned long flags; /* low level flags */ unsigned long local_flags; /* non-racy flags */ int cpu; /* cpu we're on */ - int preempt_count; + int preempt_count; /* 0 => preemptable, + <0 => BUG */ struct restart_block restart_block; }; diff --git a/include/asm-ppc64/compat.h b/include/asm-ppc64/compat.h index 09c28d28ce6c..12414f5fc666 100644 --- a/include/asm-ppc64/compat.h +++ b/include/asm-ppc64/compat.h @@ -26,6 +26,7 @@ typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; typedef s32 compat_key_t; +typedef s32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; diff --git a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h index 5d3cd9d042e2..553b2ea23bed 100644 --- a/include/asm-ppc64/machdep.h +++ b/include/asm-ppc64/machdep.h @@ -76,6 +76,7 @@ struct machdep_calls { void (*tce_flush)(struct iommu_table *tbl); void (*iommu_dev_setup)(struct pci_dev *dev); void (*iommu_bus_setup)(struct pci_bus *bus); + void (*irq_bus_setup)(struct pci_bus *bus); int (*probe)(int platform); void (*setup_arch)(void); diff --git a/include/asm-ppc64/mmu.h b/include/asm-ppc64/mmu.h index c78282a67d8e..9d03a98a4fa3 100644 --- a/include/asm-ppc64/mmu.h +++ b/include/asm-ppc64/mmu.h @@ -47,9 +47,10 @@ #define SLB_VSID_KS ASM_CONST(0x0000000000000800) #define SLB_VSID_KP ASM_CONST(0x0000000000000400) #define SLB_VSID_N ASM_CONST(0x0000000000000200) /* no-execute */ -#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage 16M */ +#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage */ #define SLB_VSID_C ASM_CONST(0x0000000000000080) /* class */ - +#define SLB_VSID_LS ASM_CONST(0x0000000000000070) /* size of largepage */ + #define SLB_VSID_KERNEL (SLB_VSID_KP|SLB_VSID_C) #define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS) diff --git a/include/asm-ppc64/mmzone.h b/include/asm-ppc64/mmzone.h index 0619a41a3c9d..ed473f4b0152 100644 --- a/include/asm-ppc64/mmzone.h +++ b/include/asm-ppc64/mmzone.h @@ -10,9 +10,20 @@ #include #include -#ifdef CONFIG_DISCONTIGMEM +/* generic non-linear memory support: + * + * 1) we will not split memory into more chunks than will fit into the + * flags field of the struct page + */ + + +#ifdef CONFIG_NEED_MULTIPLE_NODES extern struct pglist_data *node_data[]; +/* + * Return a pointer to the node data for node n. + */ +#define NODE_DATA(nid) (node_data[nid]) /* * Following are specific to this numa platform. @@ -47,36 +58,32 @@ static inline int pa_to_nid(unsigned long pa) return nid; } -#define pfn_to_nid(pfn) pa_to_nid((pfn) << PAGE_SHIFT) - -/* - * Return a pointer to the node data for node n. - */ -#define NODE_DATA(nid) (node_data[nid]) - #define node_localnr(pfn, nid) ((pfn) - NODE_DATA(nid)->node_start_pfn) /* * Following are macros that each numa implmentation must define. */ -/* - * Given a kernel address, find the home node of the underlying memory. - */ -#define kvaddr_to_nid(kaddr) pa_to_nid(__pa(kaddr)) - -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_end_pfn(nid) (NODE_DATA(nid)->node_end_pfn) #define local_mapnr(kvaddr) \ ( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) +#ifdef CONFIG_DISCONTIGMEM + +/* + * Given a kernel address, find the home node of the underlying memory. + */ +#define kvaddr_to_nid(kaddr) pa_to_nid(__pa(kaddr)) + +#define pfn_to_nid(pfn) pa_to_nid((unsigned long)(pfn) << PAGE_SHIFT) + /* Written this way to avoid evaluating arguments twice */ #define discontigmem_pfn_to_page(pfn) \ ({ \ unsigned long __tmp = pfn; \ - (node_mem_map(pfn_to_nid(__tmp)) + \ + (NODE_DATA(pfn_to_nid(__tmp))->node_mem_map + \ node_localnr(__tmp, pfn_to_nid(__tmp))); \ }) @@ -91,4 +98,11 @@ static inline int pa_to_nid(unsigned long pa) #define discontigmem_pfn_valid(pfn) ((pfn) < num_physpages) #endif /* CONFIG_DISCONTIGMEM */ + +#endif /* CONFIG_NEED_MULTIPLE_NODES */ + +#ifdef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID +#define early_pfn_to_nid(pfn) pa_to_nid(((unsigned long)pfn) << PAGE_SHIFT) +#endif + #endif /* _ASM_MMZONE_H_ */ diff --git a/include/asm-ppc64/nvram.h b/include/asm-ppc64/nvram.h index 4e6dd370d936..dfaa21566c9a 100644 --- a/include/asm-ppc64/nvram.h +++ b/include/asm-ppc64/nvram.h @@ -70,6 +70,7 @@ extern struct nvram_partition *nvram_find_partition(int sig, const char *name); extern int pSeries_nvram_init(void); extern int pmac_nvram_init(void); +extern int bpa_nvram_init(void); /* PowerMac specific nvram stuffs */ diff --git a/include/asm-ppc64/page.h b/include/asm-ppc64/page.h index 257d87eb7c34..a5893a305a09 100644 --- a/include/asm-ppc64/page.h +++ b/include/asm-ppc64/page.h @@ -217,7 +217,8 @@ extern u64 ppc64_pft_size; /* Log 2 of page table size */ #define page_to_pfn(page) discontigmem_page_to_pfn(page) #define pfn_to_page(pfn) discontigmem_pfn_to_page(pfn) #define pfn_valid(pfn) discontigmem_pfn_valid(pfn) -#else +#endif +#ifdef CONFIG_FLATMEM #define pfn_to_page(pfn) (mem_map + (pfn)) #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) diff --git a/include/asm-ppc64/ppc32.h b/include/asm-ppc64/ppc32.h index 1d0404897550..6b44a8caf395 100644 --- a/include/asm-ppc64/ppc32.h +++ b/include/asm-ppc64/ppc32.h @@ -32,7 +32,7 @@ typedef struct compat_siginfo { /* POSIX.1b timers */ struct { - timer_t _tid; /* timer id */ + compat_timer_t _tid; /* timer id */ int _overrun; /* overrun count */ compat_sigval_t _sigval; /* same as below */ int _sys_private; /* not to be passed to user */ diff --git a/include/asm-ppc64/processor.h b/include/asm-ppc64/processor.h index 3084099086a8..af28aa55d8c1 100644 --- a/include/asm-ppc64/processor.h +++ b/include/asm-ppc64/processor.h @@ -138,8 +138,16 @@ #define SPRN_NIADORM 0x3F3 /* Hardware Implementation Register 2 */ #define SPRN_HID4 0x3F4 /* 970 HID4 */ #define SPRN_HID5 0x3F6 /* 970 HID5 */ -#define SPRN_TSC 0x3FD /* Thread switch control */ -#define SPRN_TST 0x3FC /* Thread switch timeout */ +#define SPRN_HID6 0x3F9 /* BE HID 6 */ +#define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */ +#define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */ +#define SPRN_TSCR 0x399 /* Thread switch control on BE */ +#define SPRN_TTR 0x39A /* Thread switch timeout on BE */ +#define TSCR_DEC_ENABLE 0x200000 /* Decrementer Interrupt */ +#define TSCR_EE_ENABLE 0x100000 /* External Interrupt */ +#define TSCR_EE_BOOST 0x080000 /* External Interrupt Boost */ +#define SPRN_TSC 0x3FD /* Thread switch control on others */ +#define SPRN_TST 0x3FC /* Thread switch timeout on others */ #define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */ #define SPRN_LR 0x008 /* Link Register */ #define SPRN_PIR 0x3FF /* Processor Identification Register */ @@ -259,6 +267,7 @@ #define PV_970FX 0x003C #define PV_630 0x0040 #define PV_630p 0x0041 +#define PV_BE 0x0070 /* Platforms supported by PPC64 */ #define PLATFORM_PSERIES 0x0100 @@ -267,6 +276,7 @@ #define PLATFORM_LPAR 0x0001 #define PLATFORM_POWERMAC 0x0400 #define PLATFORM_MAPLE 0x0500 +#define PLATFORM_BPA 0x1000 /* Compatibility with drivers coming from PPC32 world */ #define _machine (systemcfg->platform) @@ -278,6 +288,7 @@ #define IC_INVALID 0 #define IC_OPEN_PIC 1 #define IC_PPC_XIC 2 +#define IC_BPA_IIC 3 #define XGLUE(a,b) a##b #define GLUE(a,b) XGLUE(a,b) diff --git a/include/asm-ppc64/rtas.h b/include/asm-ppc64/rtas.h index a8ab0e9db84a..e7d1b5222802 100644 --- a/include/asm-ppc64/rtas.h +++ b/include/asm-ppc64/rtas.h @@ -186,8 +186,14 @@ extern int rtas_get_sensor(int sensor, int index, int *state); extern int rtas_get_power_level(int powerdomain, int *level); extern int rtas_set_power_level(int powerdomain, int level, int *setlevel); extern int rtas_set_indicator(int indicator, int index, int new_value); +extern void rtas_progress(char *s, unsigned short hex); extern void rtas_initialize(void); +struct rtc_time; +extern void rtas_get_boot_time(struct rtc_time *rtc_time); +extern void rtas_get_rtc_time(struct rtc_time *rtc_time); +extern int rtas_set_rtc_time(struct rtc_time *rtc_time); + /* Given an RTAS status code of 9900..9905 compute the hinted delay */ unsigned int rtas_extended_busy_delay_time(int status); static inline int rtas_is_extended_busy(int status) diff --git a/include/asm-ppc64/smp.h b/include/asm-ppc64/smp.h index 8115ecb8feee..d86f742e9a21 100644 --- a/include/asm-ppc64/smp.h +++ b/include/asm-ppc64/smp.h @@ -85,6 +85,14 @@ extern void smp_generic_take_timebase(void); extern struct smp_ops_t *smp_ops; +#ifdef CONFIG_PPC_PSERIES +void vpa_init(int cpu); +#else +static inline void vpa_init(int cpu) +{ +} +#endif /* CONFIG_PPC_PSERIES */ + #endif /* __ASSEMBLY__ */ #endif /* !(_PPC64_SMP_H) */ diff --git a/include/asm-ppc64/sparsemem.h b/include/asm-ppc64/sparsemem.h new file mode 100644 index 000000000000..c5bd47e57f17 --- /dev/null +++ b/include/asm-ppc64/sparsemem.h @@ -0,0 +1,16 @@ +#ifndef _ASM_PPC64_SPARSEMEM_H +#define _ASM_PPC64_SPARSEMEM_H 1 + +#ifdef CONFIG_SPARSEMEM +/* + * SECTION_SIZE_BITS 2^N: how big each section will be + * MAX_PHYSADDR_BITS 2^N: how much physical address space we have + * MAX_PHYSMEM_BITS 2^N: how much memory we can have in that space + */ +#define SECTION_SIZE_BITS 24 +#define MAX_PHYSADDR_BITS 38 +#define MAX_PHYSMEM_BITS 36 + +#endif /* CONFIG_SPARSEMEM */ + +#endif /* _ASM_PPC64_SPARSEMEM_H */ diff --git a/include/asm-ppc64/thread_info.h b/include/asm-ppc64/thread_info.h index 48b7900e90ec..0494df6fca74 100644 --- a/include/asm-ppc64/thread_info.h +++ b/include/asm-ppc64/thread_info.h @@ -24,7 +24,7 @@ struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ int cpu; /* cpu we're on */ - int preempt_count; + int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; /* set by force_successful_syscall_return */ unsigned char syscall_noerror; diff --git a/include/asm-ppc64/time.h b/include/asm-ppc64/time.h index 8d6e3760ee10..c6c762cad8b0 100644 --- a/include/asm-ppc64/time.h +++ b/include/asm-ppc64/time.h @@ -34,6 +34,15 @@ struct rtc_time; extern void to_tm(int tim, struct rtc_time * tm); extern time_t last_rtc_update; +void generic_calibrate_decr(void); +void setup_default_decr(void); + +/* Some sane defaults: 125 MHz timebase, 1GHz processor */ +extern unsigned long ppc_proc_freq; +#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) +extern unsigned long ppc_tb_freq; +#define DEFAULT_TB_FREQ 125000000UL + /* * By putting all of this stuff into a single struct we * reduce the number of cache lines touched by do_gettimeofday. diff --git a/include/asm-ppc64/topology.h b/include/asm-ppc64/topology.h index d58d9dd79998..fcdcfd26a26b 100644 --- a/include/asm-ppc64/topology.h +++ b/include/asm-ppc64/topology.h @@ -59,10 +59,8 @@ static inline int node_to_first_cpu(int node) .nr_balance_failed = 0, \ } -#else /* !CONFIG_NUMA */ +#endif /* CONFIG_NUMA */ #include -#endif /* CONFIG_NUMA */ - #endif /* _ASM_PPC64_TOPOLOGY_H */ diff --git a/include/asm-s390/thread_info.h b/include/asm-s390/thread_info.h index aade85c53a63..fe101d41e849 100644 --- a/include/asm-s390/thread_info.h +++ b/include/asm-s390/thread_info.h @@ -50,7 +50,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned int cpu; /* current CPU */ - unsigned int preempt_count; /* 0 => preemptable */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; }; diff --git a/include/asm-sh/thread_info.h b/include/asm-sh/thread_info.h index 4bbbd9f3c37e..46080cefaff8 100644 --- a/include/asm-sh/thread_info.h +++ b/include/asm-sh/thread_info.h @@ -20,7 +20,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ __u32 flags; /* low level flags */ __u32 cpu; - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; __u8 supervisor_stack[0]; }; diff --git a/include/asm-sh64/thread_info.h b/include/asm-sh64/thread_info.h index 8a32d6bd0b79..10f024c6a2e3 100644 --- a/include/asm-sh64/thread_info.h +++ b/include/asm-sh64/thread_info.h @@ -22,7 +22,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ /* Put the 4 32-bit fields together to make asm offsetting easier. */ - __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ __u16 cpu; mm_segment_t addr_limit; diff --git a/include/asm-sparc/thread_info.h b/include/asm-sparc/thread_info.h index 104f03c55416..ff6ccb3d24c6 100644 --- a/include/asm-sparc/thread_info.h +++ b/include/asm-sparc/thread_info.h @@ -30,9 +30,9 @@ struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ - int cpu; /* cpu we're on */ - int preempt_count; + int preempt_count; /* 0 => preemptable, + <0 => BUG */ int softirq_count; int hardirq_count; diff --git a/include/asm-sparc64/compat.h b/include/asm-sparc64/compat.h index 22f58055b8ab..b59122dd176d 100644 --- a/include/asm-sparc64/compat.h +++ b/include/asm-sparc64/compat.h @@ -25,6 +25,7 @@ typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; typedef s32 compat_key_t; +typedef s32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; diff --git a/include/asm-sparc64/thread_info.h b/include/asm-sparc64/thread_info.h index 517caaba1c87..0cd652956929 100644 --- a/include/asm-sparc64/thread_info.h +++ b/include/asm-sparc64/thread_info.h @@ -46,7 +46,7 @@ struct thread_info { unsigned long fault_address; struct pt_regs *kregs; struct exec_domain *exec_domain; - int preempt_count; + int preempt_count; /* 0 => preemptable, <0 => BUG */ int __pad; unsigned long *utraps; diff --git a/include/asm-um/thread_info.h b/include/asm-um/thread_info.h index 1feaaf148ef1..97267f059ef5 100644 --- a/include/asm-um/thread_info.h +++ b/include/asm-um/thread_info.h @@ -17,7 +17,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ - __s32 preempt_count; /* 0 => preemptable, + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user diff --git a/include/asm-v850/thread_info.h b/include/asm-v850/thread_info.h index e2ef44593752..e4cfad94a553 100644 --- a/include/asm-v850/thread_info.h +++ b/include/asm-v850/thread_info.h @@ -30,7 +30,8 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ int cpu; /* cpu we're on */ - int preempt_count; + int preempt_count; /* 0 => preemptable, + <0 => BUG */ struct restart_block restart_block; }; diff --git a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h index 5dd7727c756b..a31bb99be53f 100644 --- a/include/asm-x86_64/bitops.h +++ b/include/asm-x86_64/bitops.h @@ -411,8 +411,6 @@ static __inline__ int ffs(int x) /* find last set bit */ #define fls(x) generic_fls(x) -#define ARCH_HAS_ATOMIC_UNSIGNED 1 - #endif /* __KERNEL__ */ #endif /* _X86_64_BITOPS_H */ diff --git a/include/asm-x86_64/ia32.h b/include/asm-x86_64/ia32.h index c0a7717923ed..6efa00fe4e7b 100644 --- a/include/asm-x86_64/ia32.h +++ b/include/asm-x86_64/ia32.h @@ -94,7 +94,7 @@ typedef struct compat_siginfo{ /* POSIX.1b timers */ struct { - int _tid; /* timer id */ + compat_timer_t _tid; /* timer id */ int _overrun; /* overrun count */ compat_sigval_t _sigval; /* same as below */ int _sys_private; /* not to be passed to user */ diff --git a/include/asm-x86_64/io.h b/include/asm-x86_64/io.h index 94202703fae2..37fc3f149a5a 100644 --- a/include/asm-x86_64/io.h +++ b/include/asm-x86_64/io.h @@ -124,12 +124,7 @@ extern inline void * phys_to_virt(unsigned long address) /* * Change "struct page" to physical address. */ -#ifdef CONFIG_DISCONTIGMEM -#include #define page_to_phys(page) ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT) -#else -#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) -#endif #include diff --git a/include/asm-x86_64/kprobes.h b/include/asm-x86_64/kprobes.h index bfea52d516f8..6d6d883fdf6d 100644 --- a/include/asm-x86_64/kprobes.h +++ b/include/asm-x86_64/kprobes.h @@ -38,6 +38,9 @@ typedef u8 kprobe_opcode_t; : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry +#define ARCH_SUPPORTS_KRETPROBES + +void kretprobe_trampoline(void); /* Architecture specific copy of original instruction*/ struct arch_specific_insn { diff --git a/include/asm-x86_64/mmzone.h b/include/asm-x86_64/mmzone.h index d95b7c240831..768413751b34 100644 --- a/include/asm-x86_64/mmzone.h +++ b/include/asm-x86_64/mmzone.h @@ -6,7 +6,7 @@ #include -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA #define VIRTUAL_BUG_ON(x) @@ -30,27 +30,23 @@ static inline __attribute__((pure)) int phys_to_nid(unsigned long addr) return nid; } -#define pfn_to_nid(pfn) phys_to_nid((unsigned long)(pfn) << PAGE_SHIFT) - -#define kvaddr_to_nid(kaddr) phys_to_nid(__pa(kaddr)) #define NODE_DATA(nid) (node_data[nid]) -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) - -#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_end_pfn(nid) (NODE_DATA(nid)->node_start_pfn + \ NODE_DATA(nid)->node_spanned_pages) -#define local_mapnr(kvaddr) \ - ( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) ) +#ifdef CONFIG_DISCONTIGMEM + +#define pfn_to_nid(pfn) phys_to_nid((unsigned long)(pfn) << PAGE_SHIFT) +#define kvaddr_to_nid(kaddr) phys_to_nid(__pa(kaddr)) /* AK: this currently doesn't deal with invalid addresses. We'll see if the 2.5 kernel doesn't pass them (2.4 used to). */ #define pfn_to_page(pfn) ({ \ int nid = phys_to_nid(((unsigned long)(pfn)) << PAGE_SHIFT); \ - ((pfn) - node_start_pfn(nid)) + node_mem_map(nid); \ + ((pfn) - node_start_pfn(nid)) + NODE_DATA(nid)->node_mem_map; \ }) #define page_to_pfn(page) \ @@ -60,4 +56,8 @@ static inline __attribute__((pure)) int phys_to_nid(unsigned long addr) ({ u8 nid__ = pfn_to_nid(pfn); \ nid__ != 0xff && (pfn) >= node_start_pfn(nid__) && (pfn) <= node_end_pfn(nid__); })) #endif + +#define local_mapnr(kvaddr) \ + ( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) ) +#endif #endif diff --git a/include/asm-x86_64/msr.h b/include/asm-x86_64/msr.h index 513e52c71821..bc700232728d 100644 --- a/include/asm-x86_64/msr.h +++ b/include/asm-x86_64/msr.h @@ -57,11 +57,6 @@ (val) = ((unsigned long)__a) | (((unsigned long)__d)<<32); \ } while(0) -#define rdpmc(counter,low,high) \ - __asm__ __volatile__("rdpmc" \ - : "=a" (low), "=d" (high) \ - : "c" (counter)) - #define write_tsc(val1,val2) wrmsr(0x10, val1, val2) #define rdpmc(counter,low,high) \ diff --git a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h index 9ce338c3a71e..60130f4ca986 100644 --- a/include/asm-x86_64/page.h +++ b/include/asm-x86_64/page.h @@ -119,7 +119,9 @@ extern __inline__ int get_order(unsigned long size) __pa(v); }) #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) -#ifndef CONFIG_DISCONTIGMEM +#define __boot_va(x) __va(x) +#define __boot_pa(x) __pa(x) +#ifdef CONFIG_FLATMEM #define pfn_to_page(pfn) (mem_map + (pfn)) #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) diff --git a/include/asm-x86_64/param.h b/include/asm-x86_64/param.h index b707f0568c9e..40b11937180d 100644 --- a/include/asm-x86_64/param.h +++ b/include/asm-x86_64/param.h @@ -1,9 +1,11 @@ +#include + #ifndef _ASMx86_64_PARAM_H #define _ASMx86_64_PARAM_H #ifdef __KERNEL__ -# define HZ 1000 /* Internal kernel timer frequency */ -# define USER_HZ 100 /* .. some user interfaces are in "ticks */ +# define HZ CONFIG_HZ /* Internal kernel timer frequency */ +# define USER_HZ 100 /* .. some user interfaces are in "ticks */ #define CLOCKS_PER_SEC (USER_HZ) /* like times() */ #endif diff --git a/include/asm-x86_64/percpu.h b/include/asm-x86_64/percpu.h index 415d73f3c8ef..9c71855736fb 100644 --- a/include/asm-x86_64/percpu.h +++ b/include/asm-x86_64/percpu.h @@ -39,7 +39,7 @@ extern void setup_per_cpu_areas(void); #define DEFINE_PER_CPU(type, name) \ __typeof__(type) per_cpu__##name -#define per_cpu(var, cpu) (*((void)cpu, &per_cpu__##var)) +#define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var #endif /* SMP */ diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h index 8b55f139968f..106f666517bb 100644 --- a/include/asm-x86_64/processor.h +++ b/include/asm-x86_64/processor.h @@ -280,6 +280,14 @@ struct thread_struct { set_fs(USER_DS); \ } while(0) +#define get_debugreg(var, register) \ + __asm__("movq %%db" #register ", %0" \ + :"=r" (var)) +#define set_debugreg(value, register) \ + __asm__("movq %0,%%db" #register \ + : /* no output */ \ + :"r" (value)) + struct task_struct; struct mm_struct; diff --git a/include/asm-x86_64/ptrace.h b/include/asm-x86_64/ptrace.h index 5bbc8d3141c8..ca6f15ff61d4 100644 --- a/include/asm-x86_64/ptrace.h +++ b/include/asm-x86_64/ptrace.h @@ -82,6 +82,7 @@ struct pt_regs { #if defined(__KERNEL__) && !defined(__ASSEMBLY__) #define user_mode(regs) (!!((regs)->cs & 3)) +#define user_mode_vm(regs) user_mode(regs) #define instruction_pointer(regs) ((regs)->rip) extern unsigned long profile_pc(struct pt_regs *regs); void signal_fault(struct pt_regs *regs, void __user *frame, char *where); diff --git a/include/asm-x86_64/sparsemem.h b/include/asm-x86_64/sparsemem.h new file mode 100644 index 000000000000..dabb16714a71 --- /dev/null +++ b/include/asm-x86_64/sparsemem.h @@ -0,0 +1,26 @@ +#ifndef _ASM_X86_64_SPARSEMEM_H +#define _ASM_X86_64_SPARSEMEM_H 1 + +#ifdef CONFIG_SPARSEMEM + +/* + * generic non-linear memory support: + * + * 1) we will not split memory into more chunks than will fit into the flags + * field of the struct page + * + * SECTION_SIZE_BITS 2^n: size of each section + * MAX_PHYSADDR_BITS 2^n: max size of physical address space + * MAX_PHYSMEM_BITS 2^n: how much memory we can have in that space + * + */ + +#define SECTION_SIZE_BITS 27 /* matt - 128 is convenient right now */ +#define MAX_PHYSADDR_BITS 40 +#define MAX_PHYSMEM_BITS 40 + +extern int early_pfn_to_nid(unsigned long pfn); + +#endif /* CONFIG_SPARSEMEM */ + +#endif /* _ASM_X86_64_SPARSEMEM_H */ diff --git a/include/asm-x86_64/thread_info.h b/include/asm-x86_64/thread_info.h index f4b3b249639c..08eb6e4f3737 100644 --- a/include/asm-x86_64/thread_info.h +++ b/include/asm-x86_64/thread_info.h @@ -29,7 +29,7 @@ struct thread_info { __u32 flags; /* low level flags */ __u32 status; /* thread synchronous flags */ __u32 cpu; /* current CPU */ - int preempt_count; + int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; struct restart_block restart_block; diff --git a/include/asm-x86_64/timex.h b/include/asm-x86_64/timex.h index 34f31a18f90b..24ecf6a637cb 100644 --- a/include/asm-x86_64/timex.h +++ b/include/asm-x86_64/timex.h @@ -26,6 +26,9 @@ static inline cycles_t get_cycles (void) extern unsigned int cpu_khz; +extern int read_current_timer(unsigned long *timer_value); +#define ARCH_HAS_READ_CURRENT_TIMER 1 + extern struct vxtime_data vxtime; #endif diff --git a/include/asm-x86_64/topology.h b/include/asm-x86_64/topology.h index 67f24e0ea819..8f77e9f6bc23 100644 --- a/include/asm-x86_64/topology.h +++ b/include/asm-x86_64/topology.h @@ -3,7 +3,7 @@ #include -#ifdef CONFIG_DISCONTIGMEM +#ifdef CONFIG_NUMA #include #include @@ -13,8 +13,8 @@ extern cpumask_t cpu_online_map; extern unsigned char cpu_to_node[]; +extern unsigned char pci_bus_to_node[]; extern cpumask_t node_to_cpumask[]; -extern cpumask_t pci_bus_to_cpumask[]; #ifdef CONFIG_ACPI_NUMA extern int __node_distance(int, int); @@ -26,18 +26,9 @@ extern int __node_distance(int, int); #define parent_node(node) (node) #define node_to_first_cpu(node) (__ffs(node_to_cpumask[node])) #define node_to_cpumask(node) (node_to_cpumask[node]) +#define pcibus_to_node(bus) pci_bus_to_node[(bus)->number] +#define pcibus_to_cpumask(bus) node_to_cpumask(pcibus_to_node(bus)); -static inline cpumask_t __pcibus_to_cpumask(int bus) -{ - cpumask_t busmask = pci_bus_to_cpumask[bus]; - cpumask_t online = cpu_online_map; - cpumask_t res; - cpus_and(res, busmask, online); - return res; -} -#define pcibus_to_cpumask(bus) __pcibus_to_cpumask(bus->number) - -#ifdef CONFIG_NUMA /* sched_domains SD_NODE_INIT for x86_64 machines */ #define SD_NODE_INIT (struct sched_domain) { \ .span = CPU_MASK_NONE, \ @@ -59,7 +50,6 @@ static inline cpumask_t __pcibus_to_cpumask(int bus) .balance_interval = 1, \ .nr_balance_failed = 0, \ } -#endif #endif diff --git a/include/asm-xtensa/a.out.h b/include/asm-xtensa/a.out.h new file mode 100644 index 000000000000..3be701dfe098 --- /dev/null +++ b/include/asm-xtensa/a.out.h @@ -0,0 +1,33 @@ +/* + * include/asm-xtensa/addrspace.h + * + * Dummy a.out file. Xtensa does not support the a.out format, but the kernel + * seems to depend on it. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_A_OUT_H +#define _XTENSA_A_OUT_H + +/* Note: the kernel needs the a.out definitions, even if only ELF is used. */ + +#define STACK_TOP TASK_SIZE + +struct exec +{ + unsigned long a_info; + unsigned a_text; + unsigned a_data; + unsigned a_bss; + unsigned a_syms; + unsigned a_entry; + unsigned a_trsize; + unsigned a_drsize; +}; + +#endif /* _XTENSA_A_OUT_H */ diff --git a/include/asm-xtensa/atomic.h b/include/asm-xtensa/atomic.h new file mode 100644 index 000000000000..d72bcb32ba4f --- /dev/null +++ b/include/asm-xtensa/atomic.h @@ -0,0 +1,272 @@ +/* + * include/asm-xtensa/atomic.h + * + * Atomic operations that C can't guarantee us. Useful for resource counting.. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_ATOMIC_H +#define _XTENSA_ATOMIC_H + +#include +#include + +typedef struct { volatile int counter; } atomic_t; + +#ifdef __KERNEL__ +#include +#include + +#define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) + +/* + * This Xtensa implementation assumes that the right mechanism + * for exclusion is for locking interrupts to level 1. + * + * Locking interrupts looks like this: + * + * rsil a15, 1 + * + * wsr a15, PS + * rsync + * + * Note that a15 is used here because the register allocation + * done by the compiler is not guaranteed and a window overflow + * may not occur between the rsil and wsr instructions. By using + * a15 in the rsil, the machine is guaranteed to be in a state + * where no register reference will cause an overflow. + */ + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. + */ +#define atomic_read(v) ((v)->counter) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. + */ +#define atomic_set(v,i) ((v)->counter = (i)) + +/** + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. + */ +extern __inline__ void atomic_add(int i, atomic_t * v) +{ + unsigned int vval; + + __asm__ __volatile__( + "rsil a15, "__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %2, 0 \n\t" + "add %0, %0, %1 \n\t" + "s32i %0, %2, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); +} + +/** + * atomic_sub - subtract the atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. + */ +extern __inline__ void atomic_sub(int i, atomic_t *v) +{ + unsigned int vval; + + __asm__ __volatile__( + "rsil a15, "__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %2, 0 \n\t" + "sub %0, %0, %1 \n\t" + "s32i %0, %2, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); +} + +/* + * We use atomic_{add|sub}_return to define other functions. + */ + +extern __inline__ int atomic_add_return(int i, atomic_t * v) +{ + unsigned int vval; + + __asm__ __volatile__( + "rsil a15,"__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %2, 0 \n\t" + "add %0, %0, %1 \n\t" + "s32i %0, %2, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); + + return vval; +} + +extern __inline__ int atomic_sub_return(int i, atomic_t * v) +{ + unsigned int vval; + + __asm__ __volatile__( + "rsil a15,"__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %2, 0 \n\t" + "sub %0, %0, %1 \n\t" + "s32i %0, %2, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); + + return vval; +} + +/** + * atomic_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v and returns + * true if the result is zero, or false for all + * other cases. + */ +#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0) + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. + */ +#define atomic_inc(v) atomic_add(1,(v)) + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. + */ +#define atomic_inc_return(v) atomic_add_return(1,(v)) + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. + */ +#define atomic_dec(v) atomic_sub(1,(v)) + +/** + * atomic_dec_return - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. + */ +#define atomic_dec_return(v) atomic_sub_return(1,(v)) + +/** + * atomic_dec_and_test - decrement and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. + */ +#define atomic_dec_and_test(v) (atomic_sub_return(1,(v)) == 0) + +/** + * atomic_inc_and_test - increment and test + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1 + * and returns true if the result is zero, or false for all + * other cases. + */ +#define atomic_inc_and_test(v) (atomic_add_return(1,(v)) == 0) + +/** + * atomic_add_negative - add and test if negative + * @v: pointer of type atomic_t + * @i: integer value to add + * + * Atomically adds @i to @v and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. + */ +#define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0) + + +extern __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v) +{ + unsigned int all_f = -1; + unsigned int vval; + + __asm__ __volatile__( + "rsil a15,"__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %2, 0 \n\t" + "xor %1, %4, %3 \n\t" + "and %0, %0, %4 \n\t" + "s32i %0, %2, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n" + : "=&a" (vval), "=a" (mask) + : "a" (v), "a" (all_f), "1" (mask) + : "a15", "memory" + ); +} + +extern __inline__ void atomic_set_mask(unsigned int mask, atomic_t *v) +{ + unsigned int vval; + + __asm__ __volatile__( + "rsil a15,"__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %2, 0 \n\t" + "or %0, %0, %1 \n\t" + "s32i %0, %2, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n" + : "=&a" (vval) + : "a" (mask), "a" (v) + : "a15", "memory" + ); +} + +/* Atomic operations are already serializing */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif /* __KERNEL__ */ + +#endif /* _XTENSA_ATOMIC_H */ + diff --git a/include/asm-xtensa/bitops.h b/include/asm-xtensa/bitops.h new file mode 100644 index 000000000000..d395ef226c32 --- /dev/null +++ b/include/asm-xtensa/bitops.h @@ -0,0 +1,446 @@ +/* + * include/asm-xtensa/bitops.h + * + * Atomic operations that C can't guarantee us.Useful for resource counting etc. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_BITOPS_H +#define _XTENSA_BITOPS_H + +#ifdef __KERNEL__ + +#include +#include +#include + +#ifdef CONFIG_SMP +# error SMP not supported on this architecture +#endif + +static __inline__ void set_bit(int nr, volatile void * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long flags; + + local_irq_save(flags); + *a |= mask; + local_irq_restore(flags); +} + +static __inline__ void __set_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + + *a |= mask; +} + +static __inline__ void clear_bit(int nr, volatile void * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long flags; + + local_irq_save(flags); + *a &= ~mask; + local_irq_restore(flags); +} + +static __inline__ void __clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + + *a &= ~mask; +} + +/* + * clear_bit() doesn't provide any barrier for the compiler. + */ + +#define smp_mb__before_clear_bit() barrier() +#define smp_mb__after_clear_bit() barrier() + +static __inline__ void change_bit(int nr, volatile void * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long flags; + + local_irq_save(flags); + *a ^= mask; + local_irq_restore(flags); +} + +static __inline__ void __change_bit(int nr, volatile void * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + + *a ^= mask; +} + +static __inline__ int test_and_set_bit(int nr, volatile void * addr) +{ + unsigned long retval; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long flags; + + local_irq_save(flags); + retval = (mask & *a) != 0; + *a |= mask; + local_irq_restore(flags); + + return retval; +} + +static __inline__ int __test_and_set_bit(int nr, volatile void * addr) +{ + unsigned long retval; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + + retval = (mask & *a) != 0; + *a |= mask; + + return retval; +} + +static __inline__ int test_and_clear_bit(int nr, volatile void * addr) +{ + unsigned long retval; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long flags; + + local_irq_save(flags); + retval = (mask & *a) != 0; + *a &= ~mask; + local_irq_restore(flags); + + return retval; +} + +static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long old = *a; + + *a = old & ~mask; + return (old & mask) != 0; +} + +static __inline__ int test_and_change_bit(int nr, volatile void * addr) +{ + unsigned long retval; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long flags; + + local_irq_save(flags); + + retval = (mask & *a) != 0; + *a ^= mask; + local_irq_restore(flags); + + return retval; +} + +/* + * non-atomic version; can be reordered + */ + +static __inline__ int __test_and_change_bit(int nr, volatile void *addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *a = ((unsigned long *)addr) + (nr >> 5); + unsigned long old = *a; + + *a = old ^ mask; + return (old & mask) != 0; +} + +static __inline__ int test_bit(int nr, const volatile void *addr) +{ + return 1UL & (((const volatile unsigned int *)addr)[nr>>5] >> (nr&31)); +} + +#if XCHAL_HAVE_NSAU + +static __inline__ int __cntlz (unsigned long x) +{ + int lz; + asm ("nsau %0, %1" : "=r" (lz) : "r" (x)); + return 31 - lz; +} + +#else + +static __inline__ int __cntlz (unsigned long x) +{ + unsigned long sum, x1, x2, x4, x8, x16; + x1 = x & 0xAAAAAAAA; + x2 = x & 0xCCCCCCCC; + x4 = x & 0xF0F0F0F0; + x8 = x & 0xFF00FF00; + x16 = x & 0xFFFF0000; + sum = x2 ? 2 : 0; + sum += (x16 != 0) * 16; + sum += (x8 != 0) * 8; + sum += (x4 != 0) * 4; + sum += (x1 != 0); + + return sum; +} + +#endif + +/* + * ffz: Find first zero in word. Undefined if no zero exists. + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ + +static __inline__ int ffz(unsigned long x) +{ + if ((x = ~x) == 0) + return 32; + return __cntlz(x & -x); +} + +/* + * __ffs: Find first bit set in word. Return 0 for bit 0 + */ + +static __inline__ int __ffs(unsigned long x) +{ + return __cntlz(x & -x); +} + +/* + * ffs: Find first bit set in word. This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ + +static __inline__ int ffs(unsigned long x) +{ + return __cntlz(x & -x) + 1; +} + +/* + * fls: Find last (most-significant) bit set in word. + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + +static __inline__ int fls (unsigned int x) +{ + return __cntlz(x); +} + +static __inline__ int +find_next_bit(const unsigned long *addr, int size, int offset) +{ + const unsigned long *p = addr + (offset >> 5); + unsigned long result = offset & ~31UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *p++; + tmp &= ~0UL << offset; + if (size < 32) + goto found_first; + if (tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size >= 32) { + if ((tmp = *p++) != 0) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= ~0UL >> (32 - size); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first set bit, not the number of the byte + * containing a bit. + */ + +#define find_first_bit(addr, size) \ + find_next_bit((addr), (size), 0) + +static __inline__ int +find_next_zero_bit(const unsigned long *addr, int size, int offset) +{ + const unsigned long *p = addr + (offset >> 5); + unsigned long result = offset & ~31UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *p++; + tmp |= ~0UL >> (32-offset); + if (size < 32) + goto found_first; + if (~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size & ~31UL) { + if (~(tmp = *p++)) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} + +#define find_first_zero_bit(addr, size) \ + find_next_zero_bit((addr), (size), 0) + +#ifdef __XTENSA_EL__ +# define ext2_set_bit(nr,addr) __test_and_set_bit((nr), (addr)) +# define ext2_set_bit_atomic(lock,nr,addr) test_and_set_bit((nr),(addr)) +# define ext2_clear_bit(nr,addr) __test_and_clear_bit((nr), (addr)) +# define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_bit((nr),(addr)) +# define ext2_test_bit(nr,addr) test_bit((nr), (addr)) +# define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr),(size)) +# define ext2_find_next_zero_bit(addr, size, offset) \ + find_next_zero_bit((addr), (size), (offset)) +#elif defined(__XTENSA_EB__) +# define ext2_set_bit(nr,addr) __test_and_set_bit((nr) ^ 0x18, (addr)) +# define ext2_set_bit_atomic(lock,nr,addr) test_and_set_bit((nr) ^ 0x18, (addr)) +# define ext2_clear_bit(nr,addr) __test_and_clear_bit((nr) ^ 18, (addr)) +# define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_bit((nr)^0x18,(addr)) +# define ext2_test_bit(nr,addr) test_bit((nr) ^ 0x18, (addr)) +# define ext2_find_first_zero_bit(addr, size) \ + ext2_find_next_zero_bit((addr), (size), 0) + +static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 5); + unsigned long result = offset & ~31UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if(offset) { + /* We hold the little endian value in tmp, but then the + * shift is illegal. So we could keep a big endian value + * in tmp, like this: + * + * tmp = __swab32(*(p++)); + * tmp |= ~0UL >> (32-offset); + * + * but this would decrease preformance, so we change the + * shift: + */ + tmp = *(p++); + tmp |= __swab32(~0UL >> (32-offset)); + if(size < 32) + goto found_first; + if(~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while(size & ~31UL) { + if(~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if(!size) + return result; + tmp = *p; + +found_first: + /* tmp is little endian, so we would have to swab the shift, + * see above. But then we have to swab tmp below for ffz, so + * we might as well do this here. + */ + return result + ffz(__swab32(tmp) | (~0UL << size)); +found_middle: + return result + ffz(__swab32(tmp)); +} + +#else +# error processor byte order undefined! +#endif + + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + +/* + * Find the first bit set in a 140-bit bitmap. + * The first 100 bits are unlikely to be set. + */ + +static inline int sched_find_first_bit(const unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (b[3]) + return __ffs(b[3]) + 96; + return __ffs(b[4]) + 128; +} + + +/* Bitmap functions for the minix filesystem. */ + +#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_bit(nr,addr) test_bit(nr,addr) +#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) + +#endif /* __KERNEL__ */ + +#endif /* _XTENSA_BITOPS_H */ diff --git a/include/asm-xtensa/bootparam.h b/include/asm-xtensa/bootparam.h new file mode 100644 index 000000000000..9983f2c1b7ee --- /dev/null +++ b/include/asm-xtensa/bootparam.h @@ -0,0 +1,61 @@ +/* + * include/asm-xtensa/bootparam.h + * + * Definition of the Linux/Xtensa boot parameter structure + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + * + * (Concept borrowed from the 68K port) + */ + +#ifndef _XTENSA_BOOTPARAM_H +#define _XTENSA_BOOTPARAM_H + +#define BP_VERSION 0x0001 + +#define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ +#define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ +#define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ +#define BP_TAG_SERIAL_BAUSRATE 0x1004 /* baud rate of current console. */ +#define BP_TAG_SERIAL_PORT 0x1005 /* serial device of current console */ + +#define BP_TAG_FIRST 0x7B0B /* first tag with a version number */ +#define BP_TAG_LAST 0x7E0B /* last tag */ + +#ifndef __ASSEMBLY__ + +/* All records are aligned to 4 bytes */ + +typedef struct bp_tag { + unsigned short id; /* tag id */ + unsigned short size; /* size of this record excluding the structure*/ + unsigned long data[0]; /* data */ +} bp_tag_t; + +typedef struct meminfo { + unsigned long type; + unsigned long start; + unsigned long end; +} meminfo_t; + +#define SYSMEM_BANKS_MAX 5 + +#define MEMORY_TYPE_CONVENTIONAL 0x1000 +#define MEMORY_TYPE_NONE 0x2000 + +typedef struct sysmem_info { + int nr_banks; + meminfo_t bank[SYSMEM_BANKS_MAX]; +} sysmem_info_t; + +extern sysmem_info_t sysmem; + +#endif +#endif + + + diff --git a/include/asm-xtensa/bug.h b/include/asm-xtensa/bug.h new file mode 100644 index 000000000000..56703659b204 --- /dev/null +++ b/include/asm-xtensa/bug.h @@ -0,0 +1,41 @@ +/* + * include/asm-xtensa/bug.h + * + * Macros to cause a 'bug' message. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_BUG_H +#define _XTENSA_BUG_H + +#include + +#define ILL __asm__ __volatile__ (".byte 0,0,0\n") + +#ifdef CONFIG_KALLSYMS +# define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + ILL; \ +} while (0) +#else +# define BUG() do { \ + printk("kernel BUG!\n"); \ + ILL; \ +} while (0) +#endif + +#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) +#define PAGE_BUG(page) do { BUG(); } while (0) +#define WARN_ON(condition) do { \ + if (unlikely((condition)!=0)) { \ + printk ("Warning in %s at %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \ + dump_stack(); \ + } \ +} while (0) + +#endif /* _XTENSA_BUG_H */ diff --git a/include/asm-xtensa/bugs.h b/include/asm-xtensa/bugs.h new file mode 100644 index 000000000000..c42285320133 --- /dev/null +++ b/include/asm-xtensa/bugs.h @@ -0,0 +1,22 @@ +/* + * include/asm-xtensa/bugs.h + * + * This is included by init/main.c to check for architecture-dependent bugs. + * + * Xtensa processors don't have any bugs. :) + * + * 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 _XTENSA_BUGS_H +#define _XTENSA_BUGS_H + +#include + +static void __init check_bugs(void) +{ +} + +#endif /* _XTENSA_BUGS_H */ diff --git a/include/asm-xtensa/byteorder.h b/include/asm-xtensa/byteorder.h new file mode 100644 index 000000000000..0b1552569aae --- /dev/null +++ b/include/asm-xtensa/byteorder.h @@ -0,0 +1,82 @@ +/* + * include/asm-xtensa/byteorder.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_BYTEORDER_H +#define _XTENSA_BYTEORDER_H + +#include +#include + +static __inline__ __const__ __u32 ___arch__swab32(__u32 x) +{ + __u32 res; + /* instruction sequence from Xtensa ISA release 2/2000 */ + __asm__("ssai 8 \n\t" + "srli %0, %1, 16 \n\t" + "src %0, %0, %1 \n\t" + "src %0, %0, %0 \n\t" + "src %0, %1, %0 \n" + : "=&a" (res) + : "a" (x) + ); + return res; +} + +static __inline__ __const__ __u16 ___arch__swab16(__u16 x) +{ + /* Given that 'short' values are signed (i.e., can be negative), + * we cannot assume that the upper 16-bits of the register are + * zero. We are careful to mask values after shifting. + */ + + /* There exists an anomaly between xt-gcc and xt-xcc. xt-gcc + * inserts an extui instruction after putting this function inline + * to ensure that it uses only the least-significant 16 bits of + * the result. xt-xcc doesn't use an extui, but assumes the + * __asm__ macro follows convention that the upper 16 bits of an + * 'unsigned short' result are still zero. This macro doesn't + * follow convention; indeed, it leaves garbage in the upport 16 + * bits of the register. + + * Declaring the temporary variables 'res' and 'tmp' to be 32-bit + * types while the return type of the function is a 16-bit type + * forces both compilers to insert exactly one extui instruction + * (or equivalent) to mask off the upper 16 bits. */ + + __u32 res; + __u32 tmp; + + __asm__("extui %1, %2, 8, 8\n\t" + "slli %0, %2, 8 \n\t" + "or %0, %0, %1 \n" + : "=&a" (res), "=&a" (tmp) + : "a" (x) + ); + + return res; +} + +#define __arch__swab32(x) ___arch__swab32(x) +#define __arch__swab16(x) ___arch__swab16(x) + +#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) +# define __BYTEORDER_HAS_U64__ +# define __SWAB_64_THRU_32__ +#endif + +#ifdef __XTENSA_EL__ +# include +#elif defined(__XTENSA_EB__) +# include +#else +# error processor byte order undefined! +#endif + +#endif /* __ASM_XTENSA_BYTEORDER_H */ diff --git a/include/asm-xtensa/cache.h b/include/asm-xtensa/cache.h new file mode 100644 index 000000000000..5aae3f12407c --- /dev/null +++ b/include/asm-xtensa/cache.h @@ -0,0 +1,32 @@ +/* + * include/asm-xtensa/cacheflush.h + * + * 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. + * 2 of the License, or (at your option) any later version. + * + * (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_CACHE_H +#define _XTENSA_CACHE_H + +#include + +#if XCHAL_ICACHE_SIZE > 0 +# if (XCHAL_ICACHE_SIZE % (XCHAL_ICACHE_LINESIZE*XCHAL_ICACHE_WAYS*4)) != 0 +# error cache configuration outside expected/supported range! +# endif +#endif + +#if XCHAL_DCACHE_SIZE > 0 +# if (XCHAL_DCACHE_SIZE % (XCHAL_DCACHE_LINESIZE*XCHAL_DCACHE_WAYS*4)) != 0 +# error cache configuration outside expected/supported range! +# endif +#endif + +#define L1_CACHE_SHIFT XCHAL_CACHE_LINEWIDTH_MAX +#define L1_CACHE_BYTES XCHAL_CACHE_LINESIZE_MAX + +#endif /* _XTENSA_CACHE_H */ diff --git a/include/asm-xtensa/cacheflush.h b/include/asm-xtensa/cacheflush.h new file mode 100644 index 000000000000..44a36e087844 --- /dev/null +++ b/include/asm-xtensa/cacheflush.h @@ -0,0 +1,122 @@ +/* + * include/asm-xtensa/cacheflush.h + * + * 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. + * + * (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_CACHEFLUSH_H +#define _XTENSA_CACHEFLUSH_H + +#ifdef __KERNEL__ + +#include +#include +#include + +/* + * flush and invalidate data cache, invalidate instruction cache: + * + * __flush_invalidate_cache_all() + * __flush_invalidate_cache_range(from,sze) + * + * invalidate data or instruction cache: + * + * __invalidate_icache_all() + * __invalidate_icache_page(adr) + * __invalidate_dcache_page(adr) + * __invalidate_icache_range(from,size) + * __invalidate_dcache_range(from,size) + * + * flush data cache: + * + * __flush_dcache_page(adr) + * + * flush and invalidate data cache: + * + * __flush_invalidate_dcache_all() + * __flush_invalidate_dcache_page(adr) + * __flush_invalidate_dcache_range(from,size) + */ + +extern void __flush_invalidate_cache_all(void); +extern void __flush_invalidate_cache_range(unsigned long, unsigned long); +extern void __flush_invalidate_dcache_all(void); +extern void __invalidate_icache_all(void); + +extern void __invalidate_dcache_page(unsigned long); +extern void __invalidate_icache_page(unsigned long); +extern void __invalidate_icache_range(unsigned long, unsigned long); +extern void __invalidate_dcache_range(unsigned long, unsigned long); + +#if XCHAL_DCACHE_IS_WRITEBACK +extern void __flush_dcache_page(unsigned long); +extern void __flush_invalidate_dcache_page(unsigned long); +extern void __flush_invalidate_dcache_range(unsigned long, unsigned long); +#else +# define __flush_dcache_page(p) do { } while(0) +# define __flush_invalidate_dcache_page(p) do { } while(0) +# define __flush_invalidate_dcache_range(p,s) do { } while(0) +#endif + +/* + * We have physically tagged caches - nothing to do here - + * unless we have cache aliasing. + * + * Pages can get remapped. Because this might change the 'color' of that page, + * we have to flush the cache before the PTE is changed. + * (see also Documentation/cachetlb.txt) + */ + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK + +#define flush_cache_all() __flush_invalidate_cache_all(); +#define flush_cache_mm(mm) __flush_invalidate_cache_all(); + +#define flush_cache_vmap(start,end) __flush_invalidate_cache_all(); +#define flush_cache_vunmap(start,end) __flush_invalidate_cache_all(); + +extern void flush_dcache_page(struct page*); + +extern void flush_cache_range(struct vm_area_struct*, ulong, ulong); +extern void flush_cache_page(struct vm_area_struct*, unsigned long, unsigned long); + +#else + +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) + +#define flush_cache_vmap(start,end) do { } while (0) +#define flush_cache_vunmap(start,end) do { } while (0) + +#define flush_dcache_page(page) do { } while (0) + +#define flush_cache_page(vma,addr,pfn) do { } while (0) +#define flush_cache_range(vma,start,end) do { } while (0) + +#endif + +#define flush_icache_range(start,end) \ + __invalidate_icache_range(start,(end)-(start)) + +/* This is not required, see Documentation/cachetlb.txt */ + +#define flush_icache_page(vma,page) do { } while(0) + +#define flush_dcache_mmap_lock(mapping) do { } while (0) +#define flush_dcache_mmap_unlock(mapping) do { } while (0) + + +#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len) + +#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len) + +#endif /* __KERNEL__ */ + +#endif /* _XTENSA_CACHEFLUSH_H */ + diff --git a/include/asm-xtensa/checksum.h b/include/asm-xtensa/checksum.h new file mode 100644 index 000000000000..1a00fad19929 --- /dev/null +++ b/include/asm-xtensa/checksum.h @@ -0,0 +1,264 @@ +/* + * include/asm-xtensa/checksum.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_CHECKSUM_H +#define _XTENSA_CHECKSUM_H + +#include +#include +#include + +/* + * 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 + */ +asmlinkage unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum); + +/* + * the same as csum_partial, but copies from src while it + * checksums, and handles user-space pointer exceptions correctly, when needed. + * + * here even more important to align src and dst on a 32-bit (or even + * better 64-bit) boundary + */ + +asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum, + int *src_err_ptr, int *dst_err_ptr); + +/* + * Note: when you get a NULL pointer exception here this means someone + * passed in an incorrect kernel address to one of these functions. + * + * If you use these functions directly please don't forget the + * verify_area(). + */ +extern __inline__ +unsigned int csum_partial_copy_nocheck ( const char *src, char *dst, + int len, int sum) +{ + return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL); +} + +extern __inline__ +unsigned int csum_partial_copy_from_user ( const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL); +} + +/* + * These are the old (and unsafe) way of doing checksums, a warning message will be + * printed if they are used and an exeption occurs. + * + * these functions should go away after some time. + */ + +#define csum_partial_copy_fromuser csum_partial_copy +unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum); + +/* + * Fold a partial checksum + */ + +static __inline__ unsigned int csum_fold(unsigned int sum) +{ + unsigned int __dummy; + __asm__("extui %1, %0, 16, 16\n\t" + "extui %0 ,%0, 0, 16\n\t" + "add %0, %0, %1\n\t" + "slli %1, %0, 16\n\t" + "add %0, %0, %1\n\t" + "extui %0, %0, 16, 16\n\t" + "neg %0, %0\n\t" + "addi %0, %0, -1\n\t" + "extui %0, %0, 0, 16\n\t" + : "=r" (sum), "=&r" (__dummy) + : "0" (sum)); + return sum; +} + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + */ +static __inline__ unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) +{ + unsigned int sum, tmp, endaddr; + + __asm__ __volatile__( + "sub %0, %0, %0\n\t" +#if XCHAL_HAVE_LOOPS + "loopgtz %2, 2f\n\t" +#else + "beqz %2, 2f\n\t" + "slli %4, %2, 2\n\t" + "add %4, %4, %1\n\t" + "0:\t" +#endif + "l32i %3, %1, 0\n\t" + "add %0, %0, %3\n\t" + "bgeu %0, %3, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "addi %1, %1, 4\n\t" +#if !XCHAL_HAVE_LOOPS + "blt %1, %4, 0b\n\t" +#endif + "2:\t" + /* Since the input registers which are loaded with iph and ihl + are modified, we must also specify them as outputs, or gcc + will assume they contain their original values. */ + : "=r" (sum), "=r" (iph), "=r" (ihl), "=&r" (tmp), "=&r" (endaddr) + : "1" (iph), "2" (ihl)); + + return csum_fold(sum); +} + +static __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + +#ifdef __XTENSA_EL__ + unsigned long len_proto = (ntohs(len)<<16)+proto*256; +#elif defined(__XTENSA_EB__) + unsigned long len_proto = (proto<<16)+len; +#else +# error processor byte order undefined! +#endif + __asm__("add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "add %0, %0, %2\n\t" + "bgeu %0, %2, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "add %0, %0, %3\n\t" + "bgeu %0, %3, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + : "=r" (sum), "=r" (len_proto) + : "r" (daddr), "r" (saddr), "1" (len_proto), "0" (sum)); + return sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +static __inline__ unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ + +static __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len) +{ + return csum_fold (csum_partial(buff, len, 0)); +} + +#define _HAVE_ARCH_IPV6_CSUM +static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, + struct in6_addr *daddr, + __u32 len, + unsigned short proto, + unsigned int sum) +{ + unsigned int __dummy; + __asm__("l32i %1, %2, 0\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %2, 4\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %2, 8\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %2, 12\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %3, 0\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %3, 4\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %3, 8\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "l32i %1, %3, 12\n\t" + "add %0, %0, %1\n\t" + "bgeu %0, %1, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "add %0, %0, %4\n\t" + "bgeu %0, %4, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + "add %0, %0, %5\n\t" + "bgeu %0, %5, 1f\n\t" + "addi %0, %0, 1\n\t" + "1:\t" + : "=r" (sum), "=&r" (__dummy) + : "r" (saddr), "r" (daddr), + "r" (htonl(len)), "r" (htonl(proto)), "0" (sum)); + + return csum_fold(sum); +} + +/* + * Copy and checksum to user + */ +#define HAVE_CSUM_COPY_USER +static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + if (access_ok(VERIFY_WRITE, dst, len)) + return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr); + + if (len) + *err_ptr = -EFAULT; + + return -1; /* invalid checksum */ +} +#endif diff --git a/include/asm-xtensa/coprocessor.h b/include/asm-xtensa/coprocessor.h new file mode 100644 index 000000000000..a91b96dc0efe --- /dev/null +++ b/include/asm-xtensa/coprocessor.h @@ -0,0 +1,70 @@ +/* + * include/asm-xtensa/cpextra.h + * + * 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) 2003 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_COPROCESSOR_H +#define _XTENSA_COPROCESSOR_H + +#include + +#define XTOFS(last_start,last_size,align) \ + ((last_start+last_size+align-1) & -align) + +#define XTENSA_CP_EXTRA_OFFSET 0 +#define XTENSA_CP_EXTRA_ALIGN XCHAL_EXTRA_SA_ALIGN + +#define XTENSA_CPE_CP0_OFFSET \ + XTOFS(XTENSA_CP_EXTRA_OFFSET, XCHAL_EXTRA_SA_SIZE, XCHAL_CP0_SA_ALIGN) +#define XTENSA_CPE_CP1_OFFSET \ + XTOFS(XTENSA_CPE_CP0_OFFSET, XCHAL_CP0_SA_SIZE, XCHAL_CP1_SA_ALIGN) +#define XTENSA_CPE_CP2_OFFSET \ + XTOFS(XTENSA_CPE_CP1_OFFSET, XCHAL_CP1_SA_SIZE, XCHAL_CP2_SA_ALIGN) +#define XTENSA_CPE_CP3_OFFSET \ + XTOFS(XTENSA_CPE_CP2_OFFSET, XCHAL_CP2_SA_SIZE, XCHAL_CP3_SA_ALIGN) +#define XTENSA_CPE_CP4_OFFSET \ + XTOFS(XTENSA_CPE_CP3_OFFSET, XCHAL_CP3_SA_SIZE, XCHAL_CP4_SA_ALIGN) +#define XTENSA_CPE_CP5_OFFSET \ + XTOFS(XTENSA_CPE_CP4_OFFSET, XCHAL_CP4_SA_SIZE, XCHAL_CP5_SA_ALIGN) +#define XTENSA_CPE_CP6_OFFSET \ + XTOFS(XTENSA_CPE_CP5_OFFSET, XCHAL_CP5_SA_SIZE, XCHAL_CP6_SA_ALIGN) +#define XTENSA_CPE_CP7_OFFSET \ + XTOFS(XTENSA_CPE_CP6_OFFSET, XCHAL_CP6_SA_SIZE, XCHAL_CP7_SA_ALIGN) +#define XTENSA_CP_EXTRA_SIZE \ + XTOFS(XTENSA_CPE_CP7_OFFSET, XCHAL_CP7_SA_SIZE, 16) + +#if XCHAL_CP_NUM > 0 +# ifndef __ASSEMBLY__ +/* + * Tasks that own contents of (last user) each coprocessor. + * Entries are 0 for not-owned or non-existent coprocessors. + * Note: The size of this structure is fixed to 8 bytes in entry.S + */ +typedef struct { + struct task_struct *owner; /* owner */ + int offset; /* offset in cpextra space. */ +} coprocessor_info_t; +# else +# define COPROCESSOR_INFO_OWNER 0 +# define COPROCESSOR_INFO_OFFSET 4 +# define COPROCESSOR_INFO_SIZE 8 +# endif +#endif + + +#ifndef __ASSEMBLY__ +# if XCHAL_CP_NUM > 0 +struct task_struct; +extern void release_coprocessors (struct task_struct*); +extern void save_coprocessor_registers(void*, int); +# else +# define release_coprocessors(task) +# endif +#endif + +#endif /* _XTENSA_COPROCESSOR_H */ diff --git a/include/asm-xtensa/cpumask.h b/include/asm-xtensa/cpumask.h new file mode 100644 index 000000000000..ebeede397db3 --- /dev/null +++ b/include/asm-xtensa/cpumask.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/cpumask.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_CPUMASK_H +#define _XTENSA_CPUMASK_H + +#include + +#endif /* _XTENSA_CPUMASK_H */ diff --git a/include/asm-xtensa/cputime.h b/include/asm-xtensa/cputime.h new file mode 100644 index 000000000000..a7fb864a50ae --- /dev/null +++ b/include/asm-xtensa/cputime.h @@ -0,0 +1,6 @@ +#ifndef _XTENSA_CPUTIME_H +#define _XTENSA_CPUTIME_H + +#include + +#endif /* _XTENSA_CPUTIME_H */ diff --git a/include/asm-xtensa/current.h b/include/asm-xtensa/current.h new file mode 100644 index 000000000000..8d1eb5d78649 --- /dev/null +++ b/include/asm-xtensa/current.h @@ -0,0 +1,38 @@ +/* + * include/asm-xtensa/current.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_CURRENT_H +#define _XTENSA_CURRENT_H + +#ifndef __ASSEMBLY__ + +#include + +struct task_struct; + +static inline struct task_struct *get_current(void) +{ + return current_thread_info()->task; +} + +#define current get_current() + +#else + +#define CURRENT_SHIFT 13 + +#define GET_CURRENT(reg,sp) \ + GET_THREAD_INFO(reg,sp); \ + l32i reg, reg, TI_TASK \ + +#endif + + +#endif /* XTENSA_CURRENT_H */ diff --git a/include/asm-xtensa/delay.h b/include/asm-xtensa/delay.h new file mode 100644 index 000000000000..6359c55e77a8 --- /dev/null +++ b/include/asm-xtensa/delay.h @@ -0,0 +1,50 @@ +/* + * include/asm-xtensa/delay.h + * + * 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) 2001 - 2005 Tensilica Inc. + * + */ + +#ifndef _XTENSA_DELAY_H +#define _XTENSA_DELAY_H + +#include +#include +#include + +extern unsigned long loops_per_jiffy; + +extern __inline__ void __delay(unsigned long loops) +{ + /* 2 cycles per loop. */ + __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 1, 1b" + : "=r" (loops) : "0" (loops)); +} + +static __inline__ u32 xtensa_get_ccount(void) +{ + u32 ccount; + asm volatile ("rsr %0, 234; # CCOUNT\n" : "=r" (ccount)); + return ccount; +} + +/* For SMP/NUMA systems, change boot_cpu_data to something like + * local_cpu_data->... where local_cpu_data points to the current + * cpu. */ + +static __inline__ void udelay (unsigned long usecs) +{ + unsigned long start = xtensa_get_ccount(); + unsigned long cycles = usecs * (loops_per_jiffy / (1000000UL / HZ)); + + /* Note: all variables are unsigned (can wrap around)! */ + while (((unsigned long)xtensa_get_ccount()) - start < cycles) + ; +} + +#endif + diff --git a/include/asm-xtensa/div64.h b/include/asm-xtensa/div64.h new file mode 100644 index 000000000000..c4a105776383 --- /dev/null +++ b/include/asm-xtensa/div64.h @@ -0,0 +1,19 @@ +/* + * include/asm-xtensa/div64.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_DIV64_H +#define _XTENSA_DIV64_H + +#define do_div(n,base) ({ \ + int __res = n % ((unsigned int) base); \ + n /= (unsigned int) base; \ + __res; }) + +#endif diff --git a/include/asm-xtensa/dma-mapping.h b/include/asm-xtensa/dma-mapping.h new file mode 100644 index 000000000000..e86a206f1209 --- /dev/null +++ b/include/asm-xtensa/dma-mapping.h @@ -0,0 +1,182 @@ +/* + * include/asm-xtensa/dma_mapping.h + * + * 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) 2003 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_DMA_MAPPING_H +#define _XTENSA_DMA_MAPPING_H + +#include +#include +#include +#include + +/* + * DMA-consistent mapping functions. + */ + +extern void *consistent_alloc(int, size_t, dma_addr_t, unsigned long); +extern void consistent_free(void*, size_t, dma_addr_t); +extern void consistent_sync(void*, size_t, int); + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flag); + +void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle); + +static inline dma_addr_t +dma_map_single(struct device *dev, void *ptr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + consistent_sync(ptr, size, direction); + return virt_to_phys(ptr); +} + +static inline void +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +static inline int +dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + int i; + + BUG_ON(direction == DMA_NONE); + + for (i = 0; i < nents; i++, sg++ ) { + BUG_ON(!sg->page); + + sg->dma_address = page_to_phys(sg->page) + sg->offset; + consistent_sync(page_address(sg->page) + sg->offset, + sg->length, direction); + } + + return nents; +} + +static inline dma_addr_t +dma_map_page(struct device *dev, struct page *page, unsigned long offset, + size_t size, enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + return (dma_addr_t)(page_to_pfn(page)) * PAGE_SIZE + offset; +} + +static inline void +dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + + +static inline void +dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +static inline void +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + consistent_sync((void *)bus_to_virt(dma_handle), size, direction); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + consistent_sync((void *)bus_to_virt(dma_handle), size, direction); +} + +static inline void +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + + consistent_sync((void *)bus_to_virt(dma_handle)+offset,size,direction); +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + + consistent_sync((void *)bus_to_virt(dma_handle)+offset,size,direction); +} +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction dir) +{ + int i; + for (i = 0; i < nelems; i++, sg++) + consistent_sync(page_address(sg->page) + sg->offset, + sg->length, dir); +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction dir) +{ + int i; + for (i = 0; i < nelems; i++, sg++) + consistent_sync(page_address(sg->page) + sg->offset, + sg->length, dir); +} +static inline int +dma_mapping_error(dma_addr_t dma_addr) +{ + return 0; +} + +static inline int +dma_supported(struct device *dev, u64 mask) +{ + return 1; +} + +static inline int +dma_set_mask(struct device *dev, u64 mask) +{ + if(!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + + *dev->dma_mask = mask; + + return 0; +} + +static inline int +dma_get_cache_alignment(void) +{ + return L1_CACHE_BYTES; +} + +#define dma_is_consistent(d) (1) + +static inline void +dma_cache_sync(void *vaddr, size_t size, + enum dma_data_direction direction) +{ + consistent_sync(vaddr, size, direction); +} + +#endif /* _XTENSA_DMA_MAPPING_H */ diff --git a/include/asm-xtensa/dma.h b/include/asm-xtensa/dma.h new file mode 100644 index 000000000000..1c22b0234586 --- /dev/null +++ b/include/asm-xtensa/dma.h @@ -0,0 +1,61 @@ +/* + * include/asm-xtensa/dma.h + * + * 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) 2003 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_DMA_H +#define _XTENSA_DMA_H + +#include +#include /* need byte IO */ +#include + +/* + * This is only to be defined if we have PC-like DMA. + * By default this is not true on an Xtensa processor, + * however on boards with a PCI bus, such functionality + * might be emulated externally. + * + * NOTE: there still exists driver code that assumes + * this is defined, eg. drivers/sound/soundcard.c (as of 2.4). + */ +#define MAX_DMA_CHANNELS 8 + +/* + * The maximum virtual address to which DMA transfers + * can be performed on this platform. + * + * NOTE: This is board (platform) specific, not processor-specific! + * + * NOTE: This assumes DMA transfers can only be performed on + * the section of physical memory contiguously mapped in virtual + * space for the kernel. For the Xtensa architecture, this + * means the maximum possible size of this DMA area is + * the size of the statically mapped kernel segment + * (XCHAL_KSEG_{CACHED,BYPASS}_SIZE), ie. 128 MB. + * + * NOTE: When the entire KSEG area is DMA capable, we substract + * one from the max address so that the virt_to_phys() macro + * works correctly on the address (otherwise the address + * enters another area, and virt_to_phys() may not return + * the value desired). + */ +#define MAX_DMA_ADDRESS (PAGE_OFFSET + XCHAL_KSEG_CACHED_SIZE - 1) + +/* Reserve and release a DMA channel */ +extern int request_dma(unsigned int dmanr, const char * device_id); +extern void free_dma(unsigned int dmanr); + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) +#endif + + +#endif diff --git a/include/asm-xtensa/elf.h b/include/asm-xtensa/elf.h new file mode 100644 index 000000000000..64f1f53874fe --- /dev/null +++ b/include/asm-xtensa/elf.h @@ -0,0 +1,222 @@ +/* + * include/asm-xtensa/elf.h + * + * ELF register definitions + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_ELF_H +#define _XTENSA_ELF_H + +#include +#include +#include + +/* Xtensa processor ELF architecture-magic number */ + +#define EM_XTENSA 94 +#define EM_XTENSA_OLD 0xABC7 + +/* ELF register definitions. This is needed for core dump support. */ + +/* + * elf_gregset_t contains the application-level state in the following order: + * Processor info: config_version, cpuxy + * Processor state: pc, ps, exccause, excvaddr, wb, ws, + * lbeg, lend, lcount, sar + * GP regs: ar0 - arXX + */ + +typedef unsigned long elf_greg_t; + +typedef struct { + elf_greg_t xchal_config_id0; + elf_greg_t xchal_config_id1; + elf_greg_t cpux; + elf_greg_t cpuy; + elf_greg_t pc; + elf_greg_t ps; + elf_greg_t exccause; + elf_greg_t excvaddr; + elf_greg_t windowbase; + elf_greg_t windowstart; + elf_greg_t lbeg; + elf_greg_t lend; + elf_greg_t lcount; + elf_greg_t sar; + elf_greg_t syscall; + elf_greg_t ar[XCHAL_NUM_AREGS]; +} xtensa_gregset_t; + +#define ELF_NGREG (sizeof(xtensa_gregset_t) / sizeof(elf_greg_t)) + +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +/* + * Compute the size of the coprocessor and extra state layout (register info) + * table (in bytes). + * This is actually the maximum size of the table, as opposed to the size, + * which is available from the _xtensa_reginfo_table_size global variable. + * + * (See also arch/xtensa/kernel/coprocessor.S) + * + */ + +#ifndef XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM +# define XTENSA_CPE_LTABLE_SIZE 0 +#else +# define XTENSA_CPE_SEGMENT(num) (num ? (1+num) : 0) +# define XTENSA_CPE_LTABLE_ENTRIES \ + ( XTENSA_CPE_SEGMENT(XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP0_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP1_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP2_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP3_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP4_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP5_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP6_SA_CONTENTS_LIBDB_NUM) \ + + XTENSA_CPE_SEGMENT(XCHAL_CP7_SA_CONTENTS_LIBDB_NUM) \ + + 1 /* final entry */ \ + ) +# define XTENSA_CPE_LTABLE_SIZE (XTENSA_CPE_LTABLE_ENTRIES * 8) +#endif + + +/* + * Instantiations of the elf_fpregset_t type contain, in most + * architectures, the floating point (FPU) register set. + * For Xtensa, this type is extended to contain all custom state, + * ie. coprocessor and "extra" (non-coprocessor) state (including, + * for example, TIE-defined states and register files; as well + * as other optional processor state). + * This includes FPU state if a floating-point coprocessor happens + * to have been configured within the Xtensa processor. + * + * TOTAL_FPREGS_SIZE is the required size (without rounding) + * of elf_fpregset_t. It provides space for the following: + * + * a) 32-bit mask of active coprocessors for this task (similar + * to CPENABLE in single-threaded Xtensa processor systems) + * + * b) table describing the layout of custom states (ie. of + * individual registers, etc) within the save areas + * + * c) save areas for each coprocessor and for non-coprocessor + * ("extra") state + * + * Note that save areas may require up to 16-byte alignment when + * accessed by save/restore sequences. We do not need to ensure + * such alignment in an elf_fpregset_t structure because custom + * state is not directly loaded/stored into it; rather, save area + * contents are copied to elf_fpregset_t from the active save areas + * (see 'struct task_struct' definition in processor.h for that) + * using memcpy(). But we do allow space for such alignment, + * to allow optimizations of layout and copying. + */ + +#define TOTAL_FPREGS_SIZE \ + (4 + XTENSA_CPE_LTABLE_SIZE + XTENSA_CP_EXTRA_SIZE) +#define ELF_NFPREG \ + ((TOTAL_FPREGS_SIZE + sizeof(elf_fpreg_t) - 1) / sizeof(elf_fpreg_t)) + +typedef unsigned int elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; + +#define ELF_CORE_COPY_REGS(_eregs, _pregs) \ + xtensa_elf_core_copy_regs (&_eregs, _pregs); + +extern void xtensa_elf_core_copy_regs (xtensa_gregset_t *, struct pt_regs *); + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ + +#define elf_check_arch(x) ( ( (x)->e_machine == EM_XTENSA ) || \ + ( (x)->e_machine == EM_XTENSA_OLD ) ) + +/* + * These are used to set parameters in the core dumps. + */ + +#ifdef __XTENSA_EL__ +# define ELF_DATA ELFDATA2LSB +#elif defined(__XTENSA_EB__) +# define ELF_DATA ELFDATA2MSB +#else +# error processor byte order undefined! +#endif + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_XTENSA + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE PAGE_SIZE + +/* + * This is the location that an ET_DYN program is loaded if exec'ed. Typical + * use of this is to invoke "./ld.so someprog" to test out a new version of + * the loader. We need to make sure that it is out of the way of the program + * that it will "exec", and that there is sufficient room for the brk. + */ + +#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3) + +/* + * This yields a mask that user programs can use to figure out what + * instruction set this CPU supports. This could be done in user space, + * but it's not easy, and we've already done it here. + */ + +#define ELF_HWCAP (0) + +/* + * This yields a string that ld.so will use to load implementation + * specific libraries for optimization. This is more specific in + * intent than poking at uname or /proc/cpuinfo. + * For the moment, we have only optimizations for the Intel generations, + * but that could change... + */ + +#define ELF_PLATFORM (NULL) + +/* + * The Xtensa processor ABI says that when the program starts, a2 + * contains a pointer to a function which might be registered using + * `atexit'. This provides a mean for the dynamic linker to call + * DT_FINI functions for shared libraries that have been loaded before + * the code runs. + * + * A value of 0 tells we have no such handler. + * + * We might as well make sure everything else is cleared too (except + * for the stack pointer in a1), just to make things more + * deterministic. Also, clearing a0 terminates debugger backtraces. + */ + +#define ELF_PLAT_INIT(_r, load_addr) \ + do { _r->areg[0]=0; /*_r->areg[1]=0;*/ _r->areg[2]=0; _r->areg[3]=0; \ + _r->areg[4]=0; _r->areg[5]=0; _r->areg[6]=0; _r->areg[7]=0; \ + _r->areg[8]=0; _r->areg[9]=0; _r->areg[10]=0; _r->areg[11]=0; \ + _r->areg[12]=0; _r->areg[13]=0; _r->areg[14]=0; _r->areg[15]=0; \ + } while (0) + +#ifdef __KERNEL__ + +#define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) + +extern void do_copy_regs (xtensa_gregset_t*, struct pt_regs*, + struct task_struct*); +extern void do_restore_regs (xtensa_gregset_t*, struct pt_regs*, + struct task_struct*); +extern void do_save_fpregs (elf_fpregset_t*, struct pt_regs*, + struct task_struct*); +extern int do_restore_fpregs (elf_fpregset_t*, struct pt_regs*, + struct task_struct*); + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_ELF_H */ diff --git a/include/asm-xtensa/errno.h b/include/asm-xtensa/errno.h new file mode 100644 index 000000000000..ced5194d2750 --- /dev/null +++ b/include/asm-xtensa/errno.h @@ -0,0 +1,142 @@ +/* + * include/asm-xtensa/errno.h + * + * 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) 2002 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_ERRNO_H +#define _XTENSA_ERRNO_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#endif /* _XTENSA_ERRNO_H */ diff --git a/include/asm-xtensa/fcntl.h b/include/asm-xtensa/fcntl.h new file mode 100644 index 000000000000..48876bb727d2 --- /dev/null +++ b/include/asm-xtensa/fcntl.h @@ -0,0 +1,101 @@ +/* + * include/asm-xtensa/fcntl.h + * + * 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, 1996, 1997, 1998 by Ralf Baechle + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_FCNTL_H +#define _XTENSA_FCNTL_H + +/* open/fcntl - O_SYNC is only implemented on blocks devices and on files + located on an ext2 file system */ +#define O_ACCMODE 0x0003 +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_APPEND 0x0008 +#define O_SYNC 0x0010 +#define O_NONBLOCK 0x0080 +#define O_CREAT 0x0100 /* not fcntl */ +#define O_TRUNC 0x0200 /* not fcntl */ +#define O_EXCL 0x0400 /* not fcntl */ +#define O_NOCTTY 0x0800 /* not fcntl */ +#define FASYNC 0x1000 /* fcntl, for BSD compatibility */ +#define O_LARGEFILE 0x2000 /* allow large file opens - currently ignored */ +#define O_DIRECT 0x8000 /* direct disk access hint - currently ignored*/ +#define O_DIRECTORY 0x10000 /* must be a directory */ +#define O_NOFOLLOW 0x20000 /* don't follow links */ +#define O_NOATIME 0x100000 + +#define O_NDELAY O_NONBLOCK + +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get close_on_exec */ +#define F_SETFD 2 /* set/clear close_on_exec */ +#define F_GETFL 3 /* get file->f_flags */ +#define F_SETFL 4 /* set file->f_flags */ +#define F_GETLK 14 +#define F_GETLK64 15 +#define F_SETLK 6 +#define F_SETLKW 7 +#define F_SETLK64 16 +#define F_SETLKW64 17 + +#define F_SETOWN 24 /* for sockets. */ +#define F_GETOWN 23 /* for sockets. */ +#define F_SETSIG 10 /* for sockets. */ +#define F_GETSIG 11 /* for sockets. */ + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* for old implementation of bsd flock () */ +#define F_EXLCK 4 /* or 3 */ +#define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 + +/* operations for bsd flock(), also used by the kernel implementation */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* remove lock */ + +#define LOCK_MAND 32 /* This is a mandatory flock ... */ +#define LOCK_READ 64 /* which allows concurrent read operations */ +#define LOCK_WRITE 128 /* which allows concurrent write operations */ +#define LOCK_RW 192 /* which allows concurrent read & write ops */ + +typedef struct flock { + short l_type; + short l_whence; + __kernel_off_t l_start; + __kernel_off_t l_len; + long l_sysid; + __kernel_pid_t l_pid; + long pad[4]; +} flock_t; + +struct flock64 { + short l_type; + short l_whence; + __kernel_off_t l_start; + __kernel_off_t l_len; + pid_t l_pid; +}; + +#define F_LINUX_SPECIFIC_BASE 1024 + +#endif /* _XTENSA_FCNTL_H */ diff --git a/include/asm-xtensa/fixmap.h b/include/asm-xtensa/fixmap.h new file mode 100644 index 000000000000..4423b8ad4954 --- /dev/null +++ b/include/asm-xtensa/fixmap.h @@ -0,0 +1,252 @@ +/* + * include/asm-xtensa/fixmap.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_FIXMAP_H +#define _XTENSA_FIXMAP_H + +#include + +#ifdef CONFIG_MMU + +/* + * Here we define all the compile-time virtual addresses. + */ + +#if XCHAL_SEG_MAPPABLE_VADDR != 0 +# error "Current port requires virtual user space starting at 0" +#endif +#if XCHAL_SEG_MAPPABLE_SIZE < 0x80000000 +# error "Current port requires at least 0x8000000 bytes for user space" +#endif + +/* Verify instruction/data ram/rom and xlmi don't overlay vmalloc space. */ + +#define __IN_VMALLOC(addr) \ + (((addr) >= VMALLOC_START) && ((addr) < VMALLOC_END)) +#define __SPAN_VMALLOC(start,end) \ + (((start) < VMALLOC_START) && ((end) >= VMALLOC_END)) +#define INSIDE_VMALLOC(start,end) \ + (__IN_VMALLOC((start)) || __IN_VMALLOC(end) || __SPAN_VMALLOC((start),(end))) + +#if XCHAL_NUM_INSTROM +# if XCHAL_NUM_INSTROM == 1 +# if INSIDE_VMALLOC(XCHAL_INSTROM0_VADDR,XCHAL_INSTROM0_VADDR+XCHAL_INSTROM0_SIZE) +# error vmalloc range conflicts with instrom0 +# endif +# endif +# if XCHAL_NUM_INSTROM == 2 +# if INSIDE_VMALLOC(XCHAL_INSTROM1_VADDR,XCHAL_INSTROM1_VADDR+XCHAL_INSTROM1_SIZE) +# error vmalloc range conflicts with instrom1 +# endif +# endif +#endif + +#if XCHAL_NUM_INSTRAM +# if XCHAL_NUM_INSTRAM == 1 +# if INSIDE_VMALLOC(XCHAL_INSTRAM0_VADDR,XCHAL_INSTRAM0_VADDR+XCHAL_INSTRAM0_SIZE) +# error vmalloc range conflicts with instram0 +# endif +# endif +# if XCHAL_NUM_INSTRAM == 2 +# if INSIDE_VMALLOC(XCHAL_INSTRAM1_VADDR,XCHAL_INSTRAM1_VADDR+XCHAL_INSTRAM1_SIZE) +# error vmalloc range conflicts with instram1 +# endif +# endif +#endif + +#if XCHAL_NUM_DATAROM +# if XCHAL_NUM_DATAROM == 1 +# if INSIDE_VMALLOC(XCHAL_DATAROM0_VADDR,XCHAL_DATAROM0_VADDR+XCHAL_DATAROM0_SIZE) +# error vmalloc range conflicts with datarom0 +# endif +# endif +# if XCHAL_NUM_DATAROM == 2 +# if INSIDE_VMALLOC(XCHAL_DATAROM1_VADDR,XCHAL_DATAROM1_VADDR+XCHAL_DATAROM1_SIZE) +# error vmalloc range conflicts with datarom1 +# endif +# endif +#endif + +#if XCHAL_NUM_DATARAM +# if XCHAL_NUM_DATARAM == 1 +# if INSIDE_VMALLOC(XCHAL_DATARAM0_VADDR,XCHAL_DATARAM0_VADDR+XCHAL_DATARAM0_SIZE) +# error vmalloc range conflicts with dataram0 +# endif +# endif +# if XCHAL_NUM_DATARAM == 2 +# if INSIDE_VMALLOC(XCHAL_DATARAM1_VADDR,XCHAL_DATARAM1_VADDR+XCHAL_DATARAM1_SIZE) +# error vmalloc range conflicts with dataram1 +# endif +# endif +#endif + +#if XCHAL_NUM_XLMI +# if XCHAL_NUM_XLMI == 1 +# if INSIDE_VMALLOC(XCHAL_XLMI0_VADDR,XCHAL_XLMI0_VADDR+XCHAL_XLMI0_SIZE) +# error vmalloc range conflicts with xlmi0 +# endif +# endif +# if XCHAL_NUM_XLMI == 2 +# if INSIDE_VMALLOC(XCHAL_XLMI1_VADDR,XCHAL_XLMI1_VADDR+XCHAL_XLMI1_SIZE) +# error vmalloc range conflicts with xlmi1 +# endif +# endif +#endif + +#if (XCHAL_NUM_INSTROM > 2) || \ + (XCHAL_NUM_INSTRAM > 2) || \ + (XCHAL_NUM_DATARAM > 2) || \ + (XCHAL_NUM_DATAROM > 2) || \ + (XCHAL_NUM_XLMI > 2) +# error Insufficient checks on vmalloc above for more than 2 devices +#endif + +/* + * USER_VM_SIZE does not necessarily equal TASK_SIZE. We bumped + * TASK_SIZE down to 0x4000000 to simplify the handling of windowed + * call instructions (currently limited to a range of 1 GByte). User + * tasks may very well reclaim the VM space from 0x40000000 to + * 0x7fffffff in the future, so we do not want the kernel becoming + * accustomed to having any of its stuff (e.g., page tables) in this + * region. This VM region is no-man's land for now. + */ + +#define USER_VM_START XCHAL_SEG_MAPPABLE_VADDR +#define USER_VM_SIZE 0x80000000 + +/* Size of page table: */ + +#define PGTABLE_SIZE_BITS (32 - XCHAL_MMU_MIN_PTE_PAGE_SIZE + 2) +#define PGTABLE_SIZE (1L << PGTABLE_SIZE_BITS) + +/* All kernel-mappable space: */ + +#define KERNEL_ALLMAP_START (USER_VM_START + USER_VM_SIZE) +#define KERNEL_ALLMAP_SIZE (XCHAL_SEG_MAPPABLE_SIZE - KERNEL_ALLMAP_START) + +/* Carve out page table at start of kernel-mappable area: */ + +#if KERNEL_ALLMAP_SIZE < PGTABLE_SIZE +#error "Gimme some space for page table!" +#endif +#define PGTABLE_START KERNEL_ALLMAP_START + +/* Remaining kernel-mappable space: */ + +#define KERNEL_MAPPED_START (KERNEL_ALLMAP_START + PGTABLE_SIZE) +#define KERNEL_MAPPED_SIZE (KERNEL_ALLMAP_SIZE - PGTABLE_SIZE) + +#if KERNEL_MAPPED_SIZE < 0x01000000 /* 16 MB is arbitrary for now */ +# error "Shouldn't the kernel have at least *some* mappable space?" +#endif + +#define MAX_LOW_MEMORY XCHAL_KSEG_CACHED_SIZE + +#endif + +/* + * Some constants used elsewhere, but perhaps only in Xtensa header + * files, so maybe we can get rid of some and access compile-time HAL + * directly... + * + * Note: We assume that system RAM is located at the very start of the + * kernel segments !! + */ +#define KERNEL_VM_LOW XCHAL_KSEG_CACHED_VADDR +#define KERNEL_VM_HIGH XCHAL_KSEG_BYPASS_VADDR +#define KERNEL_SPACE XCHAL_KSEG_CACHED_VADDR + +/* + * Returns the physical/virtual addresses of the kernel space + * (works with the cached kernel segment only, which is the + * one normally used for kernel operation). + */ + +/* PHYSICAL BYPASS CACHED + * + * bypass vaddr bypass paddr * cached vaddr + * cached vaddr cached paddr bypass vaddr * + * bypass paddr * bypass vaddr cached vaddr + * cached paddr * bypass vaddr cached vaddr + * other * * * + */ + +#define PHYSADDR(a) \ +(((unsigned)(a) >= XCHAL_KSEG_BYPASS_VADDR \ + && (unsigned)(a) < XCHAL_KSEG_BYPASS_VADDR + XCHAL_KSEG_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_BYPASS_VADDR + XCHAL_KSEG_BYPASS_PADDR : \ + ((unsigned)(a) >= XCHAL_KSEG_CACHED_VADDR \ + && (unsigned)(a) < XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_CACHED_PADDR : \ + (unsigned)(a)) + +#define BYPASS_ADDR(a) \ +(((unsigned)(a) >= XCHAL_KSEG_BYPASS_PADDR \ + && (unsigned)(a) < XCHAL_KSEG_BYPASS_PADDR + XCHAL_KSEG_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_BYPASS_PADDR + XCHAL_KSEG_BYPASS_VADDR : \ + ((unsigned)(a) >= XCHAL_KSEG_CACHED_PADDR \ + && (unsigned)(a) < XCHAL_KSEG_CACHED_PADDR + XCHAL_KSEG_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_CACHED_PADDR + XCHAL_KSEG_BYPASS_VADDR : \ + ((unsigned)(a) >= XCHAL_KSEG_CACHED_VADDR \ + && (unsigned)(a) < XCHAL_KSEG_CACHED_VADDR+XCHAL_KSEG_CACHED_SIZE)? \ + (unsigned)(a) - XCHAL_KSEG_CACHED_VADDR+XCHAL_KSEG_BYPASS_VADDR: \ + (unsigned)(a)) + +#define CACHED_ADDR(a) \ +(((unsigned)(a) >= XCHAL_KSEG_BYPASS_PADDR \ + && (unsigned)(a) < XCHAL_KSEG_BYPASS_PADDR + XCHAL_KSEG_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_BYPASS_PADDR + XCHAL_KSEG_CACHED_VADDR : \ + ((unsigned)(a) >= XCHAL_KSEG_CACHED_PADDR \ + && (unsigned)(a) < XCHAL_KSEG_CACHED_PADDR + XCHAL_KSEG_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_CACHED_PADDR + XCHAL_KSEG_CACHED_VADDR : \ + ((unsigned)(a) >= XCHAL_KSEG_BYPASS_VADDR \ + && (unsigned)(a) < XCHAL_KSEG_BYPASS_VADDR+XCHAL_KSEG_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KSEG_BYPASS_VADDR+XCHAL_KSEG_CACHED_VADDR : \ + (unsigned)(a)) + +#define PHYSADDR_IO(a) \ +(((unsigned)(a) >= XCHAL_KIO_BYPASS_VADDR \ + && (unsigned)(a) < XCHAL_KIO_BYPASS_VADDR + XCHAL_KIO_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_BYPASS_VADDR + XCHAL_KIO_BYPASS_PADDR : \ + ((unsigned)(a) >= XCHAL_KIO_CACHED_VADDR \ + && (unsigned)(a) < XCHAL_KIO_CACHED_VADDR + XCHAL_KIO_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_CACHED_VADDR + XCHAL_KIO_CACHED_PADDR : \ + (unsigned)(a)) + +#define BYPASS_ADDR_IO(a) \ +(((unsigned)(a) >= XCHAL_KIO_BYPASS_PADDR \ + && (unsigned)(a) < XCHAL_KIO_BYPASS_PADDR + XCHAL_KIO_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_BYPASS_PADDR + XCHAL_KIO_BYPASS_VADDR : \ + ((unsigned)(a) >= XCHAL_KIO_CACHED_PADDR \ + && (unsigned)(a) < XCHAL_KIO_CACHED_PADDR + XCHAL_KIO_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_CACHED_PADDR + XCHAL_KIO_BYPASS_VADDR : \ + ((unsigned)(a) >= XCHAL_KIO_CACHED_VADDR \ + && (unsigned)(a) < XCHAL_KIO_CACHED_VADDR + XCHAL_KIO_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_CACHED_VADDR + XCHAL_KIO_BYPASS_VADDR : \ + (unsigned)(a)) + +#define CACHED_ADDR_IO(a) \ +(((unsigned)(a) >= XCHAL_KIO_BYPASS_PADDR \ + && (unsigned)(a) < XCHAL_KIO_BYPASS_PADDR + XCHAL_KIO_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_BYPASS_PADDR + XCHAL_KIO_CACHED_VADDR : \ + ((unsigned)(a) >= XCHAL_KIO_CACHED_PADDR \ + && (unsigned)(a) < XCHAL_KIO_CACHED_PADDR + XCHAL_KIO_CACHED_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_CACHED_PADDR + XCHAL_KIO_CACHED_VADDR : \ + ((unsigned)(a) >= XCHAL_KIO_BYPASS_VADDR \ + && (unsigned)(a) < XCHAL_KIO_BYPASS_VADDR + XCHAL_KIO_BYPASS_SIZE) ? \ + (unsigned)(a) - XCHAL_KIO_BYPASS_VADDR + XCHAL_KIO_CACHED_VADDR : \ + (unsigned)(a)) + +#endif /* _XTENSA_ADDRSPACE_H */ + + + + + diff --git a/include/asm-xtensa/hardirq.h b/include/asm-xtensa/hardirq.h new file mode 100644 index 000000000000..e07c76c36b95 --- /dev/null +++ b/include/asm-xtensa/hardirq.h @@ -0,0 +1,28 @@ +/* + * include/asm-xtensa/hardirq.h + * + * 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) 2002 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_HARDIRQ_H +#define _XTENSA_HARDIRQ_H + +#include +#include +#include + +/* headers.S is sensitive to the offsets of these fields */ +typedef struct { + unsigned int __softirq_pending; + unsigned int __syscall_count; + struct task_struct * __ksoftirqd_task; /* waitqueue is too large */ + unsigned int __nmi_count; /* arch dependent */ +} ____cacheline_aligned irq_cpustat_t; + +#include /* Standard mappings for irq_cpustat_t above */ + +#endif /* _XTENSA_HARDIRQ_H */ diff --git a/include/asm-xtensa/hdreg.h b/include/asm-xtensa/hdreg.h new file mode 100644 index 000000000000..64b80607b80d --- /dev/null +++ b/include/asm-xtensa/hdreg.h @@ -0,0 +1,17 @@ +/* + * include/asm-xtensa/hdreg.h + * + * 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) 2002 - 2005 Tensilica Inc. + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +#ifndef _XTENSA_HDREG_H +#define _XTENSA_HDREG_H + +typedef unsigned int ide_ioreg_t; + +#endif diff --git a/include/asm-xtensa/highmem.h b/include/asm-xtensa/highmem.h new file mode 100644 index 000000000000..0a046ca5a687 --- /dev/null +++ b/include/asm-xtensa/highmem.h @@ -0,0 +1,17 @@ +/* + * include/asm-xtensa/highmem.h + * + * 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) 2003 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_HIGHMEM_H +#define _XTENSA_HIGHMEM_H + +extern void flush_cache_kmaps(void); + +#endif + diff --git a/include/asm-xtensa/hw_irq.h b/include/asm-xtensa/hw_irq.h new file mode 100644 index 000000000000..ccf436249eaa --- /dev/null +++ b/include/asm-xtensa/hw_irq.h @@ -0,0 +1,18 @@ +/* + * include/asm-xtensa/hw_irq.h + * + * 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) 2002 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_HW_IRQ_H +#define _XTENSA_HW_IRQ_H + +static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) +{ +} + +#endif diff --git a/include/asm-xtensa/ide.h b/include/asm-xtensa/ide.h new file mode 100644 index 000000000000..b523cd4a486e --- /dev/null +++ b/include/asm-xtensa/ide.h @@ -0,0 +1,36 @@ +/* + * include/asm-xtensa/ide.h + * + * 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 - 1996 Linus Torvalds & authors + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_IDE_H +#define _XTENSA_IDE_H + +#ifdef __KERNEL__ + +#include + +#ifndef MAX_HWIFS +# define MAX_HWIFS 1 +#endif + +static __inline__ int ide_default_irq(unsigned long base) +{ + /* Unsupported! */ + return 0; +} + +static __inline__ unsigned long ide_default_io_base(int index) +{ + /* Unsupported! */ + return 0; +} + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_IDE_H */ diff --git a/include/asm-xtensa/io.h b/include/asm-xtensa/io.h new file mode 100644 index 000000000000..2c471c42ecfc --- /dev/null +++ b/include/asm-xtensa/io.h @@ -0,0 +1,197 @@ +/* + * linux/include/asm-xtensa/io.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_IO_H +#define _XTENSA_IO_H + +#ifdef __KERNEL__ +#include +#include + +#include +#include + +#define _IO_BASE 0 + + +/* + * swap functions to change byte order from little-endian to big-endian and + * vice versa. + */ + +static inline unsigned short _swapw (unsigned short v) +{ + return (v << 8) | (v >> 8); +} + +static inline unsigned int _swapl (unsigned int v) +{ + return (v << 24) | ((v & 0xff00) << 8) | ((v >> 8) & 0xff00) | (v >> 24); +} + +/* + * Change virtual addresses to physical addresses and vv. + * These are trivial on the 1:1 Linux/Xtensa mapping + */ + +extern inline unsigned long virt_to_phys(volatile void * address) +{ + return PHYSADDR((unsigned long)address); +} + +extern inline void * phys_to_virt(unsigned long address) +{ + return (void*) CACHED_ADDR(address); +} + +/* + * IO bus memory addresses are also 1:1 with the physical address + */ + +extern inline unsigned long virt_to_bus(volatile void * address) +{ + return PHYSADDR((unsigned long)address); +} + +extern inline void * bus_to_virt (unsigned long address) +{ + return (void *) CACHED_ADDR(address); +} + +/* + * Change "struct page" to physical address. + */ + +extern inline void *ioremap(unsigned long offset, unsigned long size) +{ + return (void *) CACHED_ADDR_IO(offset); +} + +extern inline void *ioremap_nocache(unsigned long offset, unsigned long size) +{ + return (void *) BYPASS_ADDR_IO(offset); +} + +extern inline void iounmap(void *addr) +{ +} + +/* + * Generic I/O + */ + +#define readb(addr) \ + ({ unsigned char __v = (*(volatile unsigned char *)(addr)); __v; }) +#define readw(addr) \ + ({ unsigned short __v = (*(volatile unsigned short *)(addr)); __v; }) +#define readl(addr) \ + ({ unsigned int __v = (*(volatile unsigned int *)(addr)); __v; }) +#define writeb(b, addr) (void)((*(volatile unsigned char *)(addr)) = (b)) +#define writew(b, addr) (void)((*(volatile unsigned short *)(addr)) = (b)) +#define writel(b, addr) (void)((*(volatile unsigned int *)(addr)) = (b)) + +static inline __u8 __raw_readb(const volatile void __iomem *addr) +{ + return *(__force volatile __u8 *)(addr); +} +static inline __u16 __raw_readw(const volatile void __iomem *addr) +{ + return *(__force volatile __u16 *)(addr); +} +static inline __u32 __raw_readl(const volatile void __iomem *addr) +{ + return *(__force volatile __u32 *)(addr); +} +static inline void __raw_writeb(__u8 b, volatile void __iomem *addr) +{ + *(__force volatile __u8 *)(addr) = b; +} +static inline void __raw_writew(__u16 b, volatile void __iomem *addr) +{ + *(__force volatile __u16 *)(addr) = b; +} +static inline void __raw_writel(__u32 b, volatile void __iomem *addr) +{ + *(__force volatile __u32 *)(addr) = b; +} + + + + +/* These are the definitions for the x86 IO instructions + * inb/inw/inl/outb/outw/outl, the "string" versions + * insb/insw/insl/outsb/outsw/outsl, and the "pausing" versions + * inb_p/inw_p/... + * The macros don't do byte-swapping. + */ + +#define inb(port) readb((u8 *)((port)+_IO_BASE)) +#define outb(val, port) writeb((val),(u8 *)((unsigned long)(port)+_IO_BASE)) +#define inw(port) readw((u16 *)((port)+_IO_BASE)) +#define outw(val, port) writew((val),(u16 *)((unsigned long)(port)+_IO_BASE)) +#define inl(port) readl((u32 *)((port)+_IO_BASE)) +#define outl(val, port) writel((val),(u32 *)((unsigned long)(port))) + +#define inb_p(port) inb((port)) +#define outb_p(val, port) outb((val), (port)) +#define inw_p(port) inw((port)) +#define outw_p(val, port) outw((val), (port)) +#define inl_p(port) inl((port)) +#define outl_p(val, port) outl((val), (port)) + +extern void insb (unsigned long port, void *dst, unsigned long count); +extern void insw (unsigned long port, void *dst, unsigned long count); +extern void insl (unsigned long port, void *dst, unsigned long count); +extern void outsb (unsigned long port, const void *src, unsigned long count); +extern void outsw (unsigned long port, const void *src, unsigned long count); +extern void outsl (unsigned long port, const void *src, unsigned long count); + +#define IO_SPACE_LIMIT ~0 + +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +/* At this point the Xtensa doesn't provide byte swap instructions */ + +#ifdef __XTENSA_EB__ +# define in_8(addr) (*(u8*)(addr)) +# define in_le16(addr) _swapw(*(u16*)(addr)) +# define in_le32(addr) _swapl(*(u32*)(addr)) +# define out_8(b, addr) *(u8*)(addr) = (b) +# define out_le16(b, addr) *(u16*)(addr) = _swapw(b) +# define out_le32(b, addr) *(u32*)(addr) = _swapl(b) +#elif defined(__XTENSA_EL__) +# define in_8(addr) (*(u8*)(addr)) +# define in_le16(addr) (*(u16*)(addr)) +# define in_le32(addr) (*(u32*)(addr)) +# define out_8(b, addr) *(u8*)(addr) = (b) +# define out_le16(b, addr) *(u16*)(addr) = (b) +# define out_le32(b, addr) *(u32*)(addr) = (b) +#else +# error processor byte order undefined! +#endif + + +/* + * * Convert a physical pointer to a virtual kernel pointer for /dev/mem + * * access + * */ +#define xlate_dev_mem_ptr(p) __va(p) + +/* + * * Convert a virtual cached pointer to an uncached pointer + * */ +#define xlate_dev_kmem_ptr(p) p + + +#endif /* __KERNEL__ */ + +#endif /* _XTENSA_IO_H */ diff --git a/include/asm-xtensa/ioctl.h b/include/asm-xtensa/ioctl.h new file mode 100644 index 000000000000..856c605d62b1 --- /dev/null +++ b/include/asm-xtensa/ioctl.h @@ -0,0 +1,83 @@ +/* + * include/asm-xtensa/ioctl.h + * + * 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) 2003 - 2005 Tensilica Inc. + * + * Derived from "include/asm-i386/ioctl.h" + */ + +#ifndef _XTENSA_IOCTL_H +#define _XTENSA_IOCTL_H + + +/* ioctl command encoding: 32 bits total, command in lower 16 bits, + * size of the parameter structure in the lower 14 bits of the + * upper 16 bits. + * Encoding the size of the parameter structure in the ioctl request + * is useful for catching programs compiled with old versions + * and to avoid overwriting user space outside the user buffer area. + * The highest 2 bits are reserved for indicating the ``access mode''. + * NOTE: This limits the max parameter size to 16kB -1 ! + */ + +/* + * The following is for compatibility across the various Linux + * platforms. The i386 ioctl numbering scheme doesn't really enforce + * a type field. De facto, however, the top 8 bits of the lower 16 + * bits are indeed used as a type field, so we might just as well make + * this explicit here. Please be sure to use the decoding macros + * below from now on. + */ +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to create numbers */ +#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) +#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) + +/* used to decode ioctl numbers.. */ +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) +#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* ...and for the drivers/sound files... */ + +#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) +#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) +#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) +#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) +#define IOCSIZE_SHIFT (_IOC_SIZESHIFT) + +#endif diff --git a/include/asm-xtensa/ioctls.h b/include/asm-xtensa/ioctls.h new file mode 100644 index 000000000000..10c443435c11 --- /dev/null +++ b/include/asm-xtensa/ioctls.h @@ -0,0 +1,112 @@ +/* + * include/asm-xtensa/ioctl.h + * + * 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) 2003 - 2005 Tensilica Inc. + * + * Derived from "include/asm-i386/ioctls.h" + */ + +#ifndef _XTENSA_IOCTLS_H +#define _XTENSA_IOCTLS_H + +#include + +#define FIOCLEX _IO('f', 1) +#define FIONCLEX _IO('f', 2) +#define FIOASYNC _IOW('f', 125, int) +#define FIONBIO _IOW('f', 126, int) +#define FIONREAD _IOR('f', 127, int) +#define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) + +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 + +#define TCGETA _IOR('t', 23, struct termio) +#define TCSETA _IOW('t', 24, struct termio) +#define TCSETAW _IOW('t', 25, struct termio) +#define TCSETAF _IOW('t', 28, struct termio) + +#define TCSBRK _IO('t', 29) +#define TCXONC _IO('t', 30) +#define TCFLSH _IO('t', 31) + +#define TIOCSWINSZ _IOW('t', 103, struct winsize) +#define TIOCGWINSZ _IOR('t', 104, struct winsize) +#define TIOCSTART _IO('t', 110) /* start output, like ^Q */ +#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */ +#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */ + +#define TIOCSPGRP _IOW('t', 118, int) +#define TIOCGPGRP _IOR('t', 119, int) + +#define TIOCEXCL _IO('T', 12) +#define TIOCNXCL _IO('T', 13) +#define TIOCSCTTY _IO('T', 14) + +#define TIOCSTI _IOW('T', 18, char) +#define TIOCMGET _IOR('T', 21, unsigned int) +#define TIOCMBIS _IOW('T', 22, unsigned int) +#define TIOCMBIC _IOW('T', 23, unsigned int) +#define TIOCMSET _IOW('T', 24, unsigned int) +# define TIOCM_LE 0x001 +# define TIOCM_DTR 0x002 +# define TIOCM_RTS 0x004 +# define TIOCM_ST 0x008 +# define TIOCM_SR 0x010 +# define TIOCM_CTS 0x020 +# define TIOCM_CAR 0x040 +# define TIOCM_RNG 0x080 +# define TIOCM_DSR 0x100 +# define TIOCM_CD TIOCM_CAR +# define TIOCM_RI TIOCM_RNG + +#define TIOCGSOFTCAR _IOR('T', 25, unsigned int) +#define TIOCSSOFTCAR _IOW('T', 26, unsigned int) +#define TIOCLINUX _IOW('T', 28, char) +#define TIOCCONS _IO('T', 29) +#define TIOCGSERIAL _IOR('T', 30, struct serial_struct) +#define TIOCSSERIAL _IOW('T', 31, struct serial_struct) +#define TIOCPKT _IOW('T', 32, int) +# define TIOCPKT_DATA 0 +# define TIOCPKT_FLUSHREAD 1 +# define TIOCPKT_FLUSHWRITE 2 +# define TIOCPKT_STOP 4 +# define TIOCPKT_START 8 +# define TIOCPKT_NOSTOP 16 +# define TIOCPKT_DOSTOP 32 + + +#define TIOCNOTTY _IO('T', 34) +#define TIOCSETD _IOW('T', 35, int) +#define TIOCGETD _IOR('T', 36, int) +#define TCSBRKP _IOW('T', 37, int) /* Needed for POSIX tcsendbreak()*/ +#define TIOCTTYGSTRUCT _IOR('T', 38, struct tty_struct) /* For debugging only*/ +#define TIOCSBRK _IO('T', 39) /* BSD compatibility */ +#define TIOCCBRK _IO('T', 40) /* BSD compatibility */ +#define TIOCGSID _IOR('T', 41, pid_t) /* Return the session ID of FD*/ +#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ + +#define TIOCSERCONFIG _IO('T', 83) +#define TIOCSERGWILD _IOR('T', 84, int) +#define TIOCSERSWILD _IOW('T', 85, int) +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR _IOR('T', 89, unsigned int) /* Get line status reg. */ + /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ +# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ +#define TIOCSERGETMULTI _IOR('T', 90, struct serial_multiport_struct) /* Get multiport config */ +#define TIOCSERSETMULTI _IOW('T', 91, struct serial_multiport_struct) /* Set multiport config */ + +#define TIOCMIWAIT _IO('T', 92) /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT _IOR('T', 93, struct async_icount) /* read serial port inline interrupt counts */ + +#endif /* _XTENSA_IOCTLS_H */ diff --git a/include/asm-xtensa/ipc.h b/include/asm-xtensa/ipc.h new file mode 100644 index 000000000000..d37bdb4d4c9c --- /dev/null +++ b/include/asm-xtensa/ipc.h @@ -0,0 +1,34 @@ +/* + * include/asm-xtensa/ipc.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_IPC_H +#define _XTENSA_IPC_H + +struct ipc_kludge { + struct msgbuf __user *msgp; + long msgtyp; +}; + +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define SEMTIMEDOP 4 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 + +#define IPCCALL(version,op) ((version)<<16 | (op)) + +#endif /* _XTENSA_IPC_H */ diff --git a/include/asm-xtensa/ipcbuf.h b/include/asm-xtensa/ipcbuf.h new file mode 100644 index 000000000000..c33aa6a42145 --- /dev/null +++ b/include/asm-xtensa/ipcbuf.h @@ -0,0 +1,37 @@ +/* + * include/asm-xtensa/ipcbuf.h + * + * The ipc64_perm structure for the Xtensa architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_IPCBUF_H +#define _XTENSA_IPCBUF_H + +/* + * Pad space is left for: + * - 32-bit mode_t and seq + * - 2 miscellaneous 32-bit values + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + */ + +struct ipc64_perm +{ + __kernel_key_t key; + __kernel_uid32_t uid; + __kernel_gid32_t gid; + __kernel_uid32_t cuid; + __kernel_gid32_t cgid; + __kernel_mode_t mode; + unsigned long seq; + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _XTENSA_IPCBUF_H */ diff --git a/include/asm-xtensa/irq.h b/include/asm-xtensa/irq.h new file mode 100644 index 000000000000..d984e955938f --- /dev/null +++ b/include/asm-xtensa/irq.h @@ -0,0 +1,37 @@ +/* + * include/asm-xtensa/irq.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_IRQ_H +#define _XTENSA_IRQ_H + +#include +#include + +#include + +#ifndef PLATFORM_NR_IRQS +# define PLATFORM_NR_IRQS 0 +#endif +#define XTENSA_NR_IRQS XCHAL_NUM_INTERRUPTS +#define NR_IRQS (XTENSA_NR_IRQS + PLATFORM_NR_IRQS) + +static __inline__ int irq_canonicalize(int irq) +{ + return (irq); +} + +struct irqaction; +#if 0 // FIXME +extern void disable_irq_nosync(unsigned int); +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); +#endif + +#endif /* _XTENSA_IRQ_H */ diff --git a/include/asm-xtensa/kmap_types.h b/include/asm-xtensa/kmap_types.h new file mode 100644 index 000000000000..9e822d2e3bce --- /dev/null +++ b/include/asm-xtensa/kmap_types.h @@ -0,0 +1,31 @@ +/* + * include/asm-xtensa/kmap_types.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_KMAP_TYPES_H +#define _XTENSA_KMAP_TYPES_H + +enum km_type { + KM_BOUNCE_READ, + KM_SKB_SUNRPC_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_BIO_SRC_IRQ, + KM_BIO_DST_IRQ, + KM_PTE0, + KM_PTE1, + KM_IRQ0, + KM_IRQ1, + KM_SOFTIRQ0, + KM_SOFTIRQ1, + KM_TYPE_NR +}; + +#endif /* _XTENSA_KMAP_TYPES_H */ diff --git a/include/asm-xtensa/linkage.h b/include/asm-xtensa/linkage.h new file mode 100644 index 000000000000..bf2128a99d79 --- /dev/null +++ b/include/asm-xtensa/linkage.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/linkage.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_LINKAGE_H +#define _XTENSA_LINKAGE_H + +/* Nothing to do here ... */ + +#endif /* _XTENSA_LINKAGE_H */ diff --git a/include/asm-xtensa/local.h b/include/asm-xtensa/local.h new file mode 100644 index 000000000000..48723e550d14 --- /dev/null +++ b/include/asm-xtensa/local.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/local.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_LOCAL_H +#define _XTENSA_LOCAL_H + +#include + +#endif /* _XTENSA_LOCAL_H */ diff --git a/include/asm-xtensa/mman.h b/include/asm-xtensa/mman.h new file mode 100644 index 000000000000..9a95a45df996 --- /dev/null +++ b/include/asm-xtensa/mman.h @@ -0,0 +1,80 @@ +/* + * include/asm-xtensa/mman.h + * + * Xtensa Processor memory-manager definitions + * + * 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 Ralf Baechle + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_MMAN_H +#define _XTENSA_MMAN_H + +/* + * Protections are chosen from these bits, OR'd together. The + * implementation does not necessarily support PROT_EXEC or PROT_WRITE + * without PROT_READ. The only guarantees are that no writing will be + * allowed without PROT_WRITE and no access will be allowed for PROT_NONE. + */ + +#define PROT_NONE 0x0 /* page can not be accessed */ +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ + +#define PROT_SEM 0x10 /* page may be used for atomic ops */ +#define PROT_GROWSDOWN 0x01000000 /* mprotect flag: extend change to start of growsdown vma */ +#define PROT_GROWSUP 0x02000000 /* mprotect flag: extend change to end fo growsup vma */ + +/* + * Flags for mmap + */ +#define MAP_SHARED 0x001 /* Share changes */ +#define MAP_PRIVATE 0x002 /* Changes are private */ +#define MAP_TYPE 0x00f /* Mask for type of mapping */ +#define MAP_FIXED 0x010 /* Interpret addr exactly */ + +/* not used by linux, but here to make sure we don't clash with ABI defines */ +#define MAP_RENAME 0x020 /* Assign page to file */ +#define MAP_AUTOGROW 0x040 /* File may grow by writing */ +#define MAP_LOCAL 0x080 /* Copy on fork/sproc */ +#define MAP_AUTORSRV 0x100 /* Logical swap reserved on demand */ + +/* These are linux-specific */ +#define MAP_NORESERVE 0x0400 /* don't check for reservations */ +#define MAP_ANONYMOUS 0x0800 /* don't use a file */ +#define MAP_GROWSDOWN 0x1000 /* stack-like segment */ +#define MAP_DENYWRITE 0x2000 /* ETXTBSY */ +#define MAP_EXECUTABLE 0x4000 /* mark it as an executable */ +#define MAP_LOCKED 0x8000 /* pages are locked */ +#define MAP_POPULATE 0x10000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x20000 /* do not block on IO */ + +/* + * Flags for msync + */ +#define MS_ASYNC 0x0001 /* sync memory asynchronously */ +#define MS_INVALIDATE 0x0002 /* invalidate mappings & caches */ +#define MS_SYNC 0x0004 /* synchronous memory sync */ + +/* + * Flags for mlockall + */ +#define MCL_CURRENT 1 /* lock all current mappings */ +#define MCL_FUTURE 2 /* lock all future mappings */ + +#define MADV_NORMAL 0x0 /* default page-in behavior */ +#define MADV_RANDOM 0x1 /* page-in minimum required */ +#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */ +#define MADV_WILLNEED 0x3 /* pre-fault pages */ +#define MADV_DONTNEED 0x4 /* discard these pages */ + +/* compatibility flags */ +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FILE 0 + +#endif /* _XTENSA_MMAN_H */ diff --git a/include/asm-xtensa/mmu.h b/include/asm-xtensa/mmu.h new file mode 100644 index 000000000000..44c5bb04c55c --- /dev/null +++ b/include/asm-xtensa/mmu.h @@ -0,0 +1,17 @@ +/* + * include/asm-xtensa/mmu.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_MMU_H +#define _XTENSA_MMU_H + +/* Default "unsigned long" context */ +typedef unsigned long mm_context_t; + +#endif /* _XTENSA_MMU_H */ diff --git a/include/asm-xtensa/mmu_context.h b/include/asm-xtensa/mmu_context.h new file mode 100644 index 000000000000..1b0801548cd9 --- /dev/null +++ b/include/asm-xtensa/mmu_context.h @@ -0,0 +1,330 @@ +/* + * include/asm-xtensa/mmu_context.h + * + * Switch an MMU context. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_MMU_CONTEXT_H +#define _XTENSA_MMU_CONTEXT_H + +#include +#include + +#include +#include +#include +#include + +/* + * Linux was ported to Xtensa assuming all auto-refill ways in set 0 + * had the same properties (a very likely assumption). Multiple sets + * of auto-refill ways will still work properly, but not as optimally + * as the Xtensa designer may have assumed. + * + * We make this case a hard #error, killing the kernel build, to alert + * the developer to this condition (which is more likely an error). + * You super-duper clever developers can change it to a warning or + * remove it altogether if you think you know what you're doing. :) + */ + +#if (XCHAL_HAVE_TLBS != 1) +# error "Linux must have an MMU!" +#endif + +#if ((XCHAL_ITLB_ARF_WAYS == 0) || (XCHAL_DTLB_ARF_WAYS == 0)) +# error "MMU must have auto-refill ways" +#endif + +#if ((XCHAL_ITLB_ARF_SETS != 1) || (XCHAL_DTLB_ARF_SETS != 1)) +# error Linux may not use all auto-refill ways as efficiently as you think +#endif + +#if (XCHAL_MMU_MAX_PTE_PAGE_SIZE != XCHAL_MMU_MIN_PTE_PAGE_SIZE) +# error Only one page size allowed! +#endif + +extern unsigned long asid_cache; +extern pgd_t *current_pgd; + +/* + * Define the number of entries per auto-refill way in set 0 of both I and D + * TLBs. We deal only with set 0 here (an assumption further explained in + * assertions.h). Also, define the total number of ARF entries in both TLBs. + */ + +#define ITLB_ENTRIES_PER_ARF_WAY (XCHAL_ITLB_SET(XCHAL_ITLB_ARF_SET0,ENTRIES)) +#define DTLB_ENTRIES_PER_ARF_WAY (XCHAL_DTLB_SET(XCHAL_DTLB_ARF_SET0,ENTRIES)) + +#define ITLB_ENTRIES \ + (ITLB_ENTRIES_PER_ARF_WAY * (XCHAL_ITLB_SET(XCHAL_ITLB_ARF_SET0,WAYS))) +#define DTLB_ENTRIES \ + (DTLB_ENTRIES_PER_ARF_WAY * (XCHAL_DTLB_SET(XCHAL_DTLB_ARF_SET0,WAYS))) + + +/* + * SMALLEST_NTLB_ENTRIES is the smaller of ITLB_ENTRIES and DTLB_ENTRIES. + * In practice, they are probably equal. This macro simplifies function + * flush_tlb_range(). + */ + +#if (DTLB_ENTRIES < ITLB_ENTRIES) +# define SMALLEST_NTLB_ENTRIES DTLB_ENTRIES +#else +# define SMALLEST_NTLB_ENTRIES ITLB_ENTRIES +#endif + + +/* + * asid_cache tracks only the ASID[USER_RING] field of the RASID special + * register, which is the current user-task asid allocation value. + * mm->context has the same meaning. When it comes time to write the + * asid_cache or mm->context values to the RASID special register, we first + * shift the value left by 8, then insert the value. + * ASID[0] always contains the kernel's asid value, and we reserve three + * other asid values that we never assign to user tasks. + */ + +#define ASID_INC 0x1 +#define ASID_MASK ((1 << XCHAL_MMU_ASID_BITS) - 1) + +/* + * XCHAL_MMU_ASID_INVALID is a configurable Xtensa processor constant + * indicating invalid address space. XCHAL_MMU_ASID_KERNEL is a configurable + * Xtensa processor constant indicating the kernel address space. They can + * be arbitrary values. + * + * We identify three more unique, reserved ASID values to use in the unused + * ring positions. No other user process will be assigned these reserved + * ASID values. + * + * For example, given that + * + * XCHAL_MMU_ASID_INVALID == 0 + * XCHAL_MMU_ASID_KERNEL == 1 + * + * the following maze of #if statements would generate + * + * ASID_RESERVED_1 == 2 + * ASID_RESERVED_2 == 3 + * ASID_RESERVED_3 == 4 + * ASID_FIRST_NONRESERVED == 5 + */ + +#if (XCHAL_MMU_ASID_INVALID != XCHAL_MMU_ASID_KERNEL + 1) +# define ASID_RESERVED_1 ((XCHAL_MMU_ASID_KERNEL + 1) & ASID_MASK) +#else +# define ASID_RESERVED_1 ((XCHAL_MMU_ASID_KERNEL + 2) & ASID_MASK) +#endif + +#if (XCHAL_MMU_ASID_INVALID != ASID_RESERVED_1 + 1) +# define ASID_RESERVED_2 ((ASID_RESERVED_1 + 1) & ASID_MASK) +#else +# define ASID_RESERVED_2 ((ASID_RESERVED_1 + 2) & ASID_MASK) +#endif + +#if (XCHAL_MMU_ASID_INVALID != ASID_RESERVED_2 + 1) +# define ASID_RESERVED_3 ((ASID_RESERVED_2 + 1) & ASID_MASK) +#else +# define ASID_RESERVED_3 ((ASID_RESERVED_2 + 2) & ASID_MASK) +#endif + +#if (XCHAL_MMU_ASID_INVALID != ASID_RESERVED_3 + 1) +# define ASID_FIRST_NONRESERVED ((ASID_RESERVED_3 + 1) & ASID_MASK) +#else +# define ASID_FIRST_NONRESERVED ((ASID_RESERVED_3 + 2) & ASID_MASK) +#endif + +#define ASID_ALL_RESERVED ( ((ASID_RESERVED_1) << 24) + \ + ((ASID_RESERVED_2) << 16) + \ + ((ASID_RESERVED_3) << 8) + \ + ((XCHAL_MMU_ASID_KERNEL)) ) + + +/* + * NO_CONTEXT is the invalid ASID value that we don't ever assign to + * any user or kernel context. NO_CONTEXT is a better mnemonic than + * XCHAL_MMU_ASID_INVALID, so we use it in code instead. + */ + +#define NO_CONTEXT XCHAL_MMU_ASID_INVALID + +#if (KERNEL_RING != 0) +# error The KERNEL_RING really should be zero. +#endif + +#if (USER_RING >= XCHAL_MMU_RINGS) +# error USER_RING cannot be greater than the highest numbered ring. +#endif + +#if (USER_RING == KERNEL_RING) +# error The user and kernel rings really should not be equal. +#endif + +#if (USER_RING == 1) +#define ASID_INSERT(x) ( ((ASID_RESERVED_1) << 24) + \ + ((ASID_RESERVED_2) << 16) + \ + (((x) & (ASID_MASK)) << 8) + \ + ((XCHAL_MMU_ASID_KERNEL)) ) + +#elif (USER_RING == 2) +#define ASID_INSERT(x) ( ((ASID_RESERVED_1) << 24) + \ + (((x) & (ASID_MASK)) << 16) + \ + ((ASID_RESERVED_2) << 8) + \ + ((XCHAL_MMU_ASID_KERNEL)) ) + +#elif (USER_RING == 3) +#define ASID_INSERT(x) ( (((x) & (ASID_MASK)) << 24) + \ + ((ASID_RESERVED_1) << 16) + \ + ((ASID_RESERVED_2) << 8) + \ + ((XCHAL_MMU_ASID_KERNEL)) ) + +#else +#error Goofy value for USER_RING + +#endif /* USER_RING == 1 */ + + +/* + * All unused by hardware upper bits will be considered + * as a software asid extension. + */ + +#define ASID_VERSION_MASK ((unsigned long)~(ASID_MASK|(ASID_MASK-1))) +#define ASID_FIRST_VERSION \ + ((unsigned long)(~ASID_VERSION_MASK) + 1 + ASID_FIRST_NONRESERVED) + +extern inline void set_rasid_register (unsigned long val) +{ + __asm__ __volatile__ (" wsr %0, "__stringify(RASID)"\n\t" + " isync\n" : : "a" (val)); +} + +extern inline unsigned long get_rasid_register (void) +{ + unsigned long tmp; + __asm__ __volatile__ (" rsr %0, "__stringify(RASID)"\n\t" : "=a" (tmp)); + return tmp; +} + + +#if ((XCHAL_MMU_ASID_INVALID == 0) && (XCHAL_MMU_ASID_KERNEL == 1)) + +extern inline void +get_new_mmu_context(struct mm_struct *mm, unsigned long asid) +{ + extern void flush_tlb_all(void); + if (! ((asid += ASID_INC) & ASID_MASK) ) { + flush_tlb_all(); /* start new asid cycle */ + if (!asid) /* fix version if needed */ + asid = ASID_FIRST_VERSION - ASID_FIRST_NONRESERVED; + asid += ASID_FIRST_NONRESERVED; + } + mm->context = asid_cache = asid; +} + +#else +#warning ASID_{INVALID,KERNEL} values impose non-optimal get_new_mmu_context implementation + +/* XCHAL_MMU_ASID_INVALID == 0 and XCHAL_MMU_ASID_KERNEL ==1 are + really the best, but if you insist... */ + +extern inline int validate_asid (unsigned long asid) +{ + switch (asid) { + case XCHAL_MMU_ASID_INVALID: + case XCHAL_MMU_ASID_KERNEL: + case ASID_RESERVED_1: + case ASID_RESERVED_2: + case ASID_RESERVED_3: + return 0; /* can't use these values as ASIDs */ + } + return 1; /* valid */ +} + +extern inline void +get_new_mmu_context(struct mm_struct *mm, unsigned long asid) +{ + extern void flush_tlb_all(void); + while (1) { + asid += ASID_INC; + if ( ! (asid & ASID_MASK) ) { + flush_tlb_all(); /* start new asid cycle */ + if (!asid) /* fix version if needed */ + asid = ASID_FIRST_VERSION - ASID_FIRST_NONRESERVED; + asid += ASID_FIRST_NONRESERVED; + break; /* no need to validate here */ + } + if (validate_asid (asid & ASID_MASK)) + break; + } + mm->context = asid_cache = asid; +} + +#endif + + +/* + * Initialize the context related info for a new mm_struct + * instance. + */ + +extern inline int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + mm->context = NO_CONTEXT; + return 0; +} + +extern inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ + unsigned long asid = asid_cache; + + /* Check if our ASID is of an older version and thus invalid */ + + if ((next->context ^ asid) & ASID_VERSION_MASK) + get_new_mmu_context(next, asid); + + set_rasid_register (ASID_INSERT(next->context)); + invalidate_page_directory(); +} + +#define deactivate_mm(tsk, mm) do { } while(0) + +/* + * Destroy context related info for an mm_struct that is about + * to be put to rest. + */ +extern inline void destroy_context(struct mm_struct *mm) +{ + /* Nothing to do. */ +} + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. + */ +extern inline void +activate_mm(struct mm_struct *prev, struct mm_struct *next) +{ + /* Unconditionally get a new ASID. */ + + get_new_mmu_context(next, asid_cache); + set_rasid_register (ASID_INSERT(next->context)); + invalidate_page_directory(); +} + + +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ + /* Nothing to do. */ + +} + +#endif /* _XTENSA_MMU_CONTEXT_H */ diff --git a/include/asm-xtensa/module.h b/include/asm-xtensa/module.h new file mode 100644 index 000000000000..ffb25bfdf6a1 --- /dev/null +++ b/include/asm-xtensa/module.h @@ -0,0 +1,25 @@ +/* + * include/asm-xtensa/module.h + * + * This file contains the module code specific to the Xtensa 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_MODULE_H +#define _XTENSA_MODULE_H + +struct mod_arch_specific +{ + /* Module support is not completely implemented. */ +}; + +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr + +#endif /* _XTENSA_MODULE_H */ diff --git a/include/asm-xtensa/msgbuf.h b/include/asm-xtensa/msgbuf.h new file mode 100644 index 000000000000..693c96755280 --- /dev/null +++ b/include/asm-xtensa/msgbuf.h @@ -0,0 +1,48 @@ +/* + * include/asm-xtensa/msgbuf.h + * + * The msqid64_ds structure for the Xtensa architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + * + * 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 _XTENSA_MSGBUF_H +#define _XTENSA_MSGBUF_H + +struct msqid64_ds { + struct ipc64_perm msg_perm; +#ifdef __XTENSA_EB__ + unsigned int __unused1; + __kernel_time_t msg_stime; /* last msgsnd time */ + unsigned int __unused2; + __kernel_time_t msg_rtime; /* last msgrcv time */ + unsigned int __unused3; + __kernel_time_t msg_ctime; /* last change time */ +#elif defined(__XTENSA_EL__) + __kernel_time_t msg_stime; /* last msgsnd time */ + unsigned int __unused1; + __kernel_time_t msg_rtime; /* last msgrcv time */ + unsigned int __unused2; + __kernel_time_t msg_ctime; /* last change time */ + unsigned int __unused3; +#else +# error processor byte order undefined! +#endif + unsigned long msg_cbytes; /* current number of bytes on queue */ + unsigned long msg_qnum; /* number of messages in queue */ + unsigned long msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + unsigned long __unused4; + unsigned long __unused5; +}; + +#endif /* _XTENSA_MSGBUF_H */ diff --git a/include/asm-xtensa/namei.h b/include/asm-xtensa/namei.h new file mode 100644 index 000000000000..3fdff039d27d --- /dev/null +++ b/include/asm-xtensa/namei.h @@ -0,0 +1,26 @@ +/* + * include/asm-xtensa/namei.h + * + * Included from linux/fs/namei.c + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_NAMEI_H +#define _XTENSA_NAMEI_H + +#ifdef __KERNEL__ + +/* This dummy routine maybe changed to something useful + * for /usr/gnemul/ emulation stuff. + * Look at asm-sparc/namei.h for details. + */ + +#define __emul_prefix() NULL + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_NAMEI_H */ diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h new file mode 100644 index 000000000000..b495e5b5a942 --- /dev/null +++ b/include/asm-xtensa/page.h @@ -0,0 +1,133 @@ +/* + * linux/include/asm-xtensa/page.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version2 as + * published by the Free Software Foundation. + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PAGE_H +#define _XTENSA_PAGE_H + +#ifdef __KERNEL__ + +#include +#include + +/* + * PAGE_SHIFT determines the page size + * PAGE_ALIGN(x) aligns the pointer to the (next) page boundary + */ + +#define PAGE_SHIFT XCHAL_MMU_MIN_PTE_PAGE_SIZE +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE - 1) & PAGE_MASK) + +#define DCACHE_WAY_SIZE (XCHAL_DCACHE_SIZE / XCHAL_DCACHE_WAYS) +#define PAGE_OFFSET XCHAL_KSEG_CACHED_VADDR + +#ifdef __ASSEMBLY__ + +#define __pgprot(x) (x) + +#else + +/* + * These are used to make use of C type-checking.. + */ + +typedef struct { unsigned long pte; } pte_t; /* page table entry */ +typedef struct { unsigned long pgd; } pgd_t; /* PGD table entry */ +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +/* + * Pure 2^n version of get_order + */ + +extern __inline__ int get_order(unsigned long size) +{ + int order; +#ifndef XCHAL_HAVE_NSU + unsigned long x1, x2, x4, x8, x16; + + size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + x1 = size & 0xAAAAAAAA; + x2 = size & 0xCCCCCCCC; + x4 = size & 0xF0F0F0F0; + x8 = size & 0xFF00FF00; + x16 = size & 0xFFFF0000; + order = x2 ? 2 : 0; + order += (x16 != 0) * 16; + order += (x8 != 0) * 8; + order += (x4 != 0) * 4; + order += (x1 != 0); + + return order; +#else + size = (size - 1) >> PAGE_SHIFT; + asm ("nsau %0, %1" : "=r" (order) : "r" (size)); + return 32 - order; +#endif +} + + +struct page; +extern void clear_page(void *page); +extern void copy_page(void *to, void *from); + +/* + * If we have cache aliasing and writeback caches, we might have to do + * some extra work + */ + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) +void clear_user_page(void *addr, unsigned long vaddr, struct page* page); +void copy_user_page(void *to,void* from,unsigned long vaddr,struct page* page); +#else +# define clear_user_page(page,vaddr,pg) clear_page(page) +# define copy_user_page(to, from, vaddr, pg) copy_page(to, from) +#endif + +/* + * This handles the memory map. We handle pages at + * XCHAL_KSEG_CACHED_VADDR for kernels with 32 bit address space. + * These macros are for conversion of kernel address, not user + * addresses. + */ + +#define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) +#define pfn_valid(pfn) ((unsigned long)pfn < max_mapnr) +#ifndef CONFIG_DISCONTIGMEM +# define pfn_to_page(pfn) (mem_map + (pfn)) +# define page_to_pfn(page) ((unsigned long)((page) - mem_map)) +#else +# error CONFIG_DISCONTIGMEM not supported +#endif + +#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) +#define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) +#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) + +#define WANT_PAGE_VIRTUAL + + +#endif /* __ASSEMBLY__ */ + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_PAGE_H */ diff --git a/include/asm-xtensa/page.h.n b/include/asm-xtensa/page.h.n new file mode 100644 index 000000000000..546cc6624f24 --- /dev/null +++ b/include/asm-xtensa/page.h.n @@ -0,0 +1,135 @@ +/* + * linux/include/asm-xtensa/page.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version2 as + * published by the Free Software Foundation. + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PAGE_H +#define _XTENSA_PAGE_H + +#ifdef __KERNEL__ + +#include +#include + +/* + * PAGE_SHIFT determines the page size + * PAGE_ALIGN(x) aligns the pointer to the (next) page boundary + */ +#define PAGE_SHIFT XCHAL_MMU_MIN_PTE_PAGE_SIZE +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE - 1) & PAGE_MASK) + +#define DCACHE_WAY_SIZE (XCHAL_DCACHE_SIZE / XCHAL_DCACHE_WAYS) +#define PAGE_OFFSET XCHAL_KSEG_CACHED_VADDR + +#ifdef __ASSEMBLY__ + +#define __pgprot(x) (x) + +#else + + +/* + * These are used to make use of C type-checking.. + */ +typedef struct { unsigned long pte; } pte_t; /* page table entry */ +typedef struct { unsigned long pmd; } pmd_t; /* PMD table entry */ +typedef struct { unsigned long pgd; } pgd_t; /* PGD table entry */ +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte) +#define pmd_val(x) ((x).pmd) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pmd(x) ((pmd_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +/* + * Pure 2^n version of get_order + */ +extern __inline__ int get_order(unsigned long size) +{ + int order; +#ifndef XCHAL_HAVE_NSU + unsigned long x1, x2, x4, x8, x16; + + size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + x1 = size & 0xAAAAAAAA; + x2 = size & 0xCCCCCCCC; + x4 = size & 0xF0F0F0F0; + x8 = size & 0xFF00FF00; + x16 = size & 0xFFFF0000; + order = x2 ? 2 : 0; + order += (x16 != 0) * 16; + order += (x8 != 0) * 8; + order += (x4 != 0) * 4; + order += (x1 != 0); + + return order; +#else + size = (size - 1) >> PAGE_SHIFT; + asm ("nsau %0, %1" : "=r" (order) : "r" (size)); + return 32 - order; +#endif +} + + +struct page; +extern void clear_page(void *page); +extern void copy_page(void *to, void *from); + +/* + * If we have cache aliasing and writeback caches, we might have to do + * some extra work + */ + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK +void clear_user_page(void *addr, unsigned long vaddr, struct page* page); +void copy_user_page(void *to, void* from, unsigned long vaddr, struct page* page); +#else +# define clear_user_page(page,vaddr,pg) clear_page(page) +# define copy_user_page(to, from, vaddr, pg) copy_page(to, from) +#endif + + +/* + * This handles the memory map. We handle pages at + * XCHAL_KSEG_CACHED_VADDR for kernels with 32 bit address space. + * These macros are for conversion of kernel address, not user + * addresses. + */ + +#define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) +#define pfn_valid(pfn) ((unsigned long)pfn < max_mapnr) +#ifndef CONFIG_DISCONTIGMEM +# define pfn_to_page(pfn) (mem_map + (pfn)) +# define page_to_pfn(page) ((unsigned long)((page) - mem_map)) +#else +# error CONFIG_DISCONTIGMEM not supported +#endif + +#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) +#define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) +#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) + +#define WANT_PAGE_VIRTUAL + + +#endif /* __ASSEMBLY__ */ + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_PAGE_H */ diff --git a/include/asm-xtensa/param.h b/include/asm-xtensa/param.h new file mode 100644 index 000000000000..c0eec8260b0e --- /dev/null +++ b/include/asm-xtensa/param.h @@ -0,0 +1,34 @@ +/* + * include/asm-xtensa/param.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PARAM_H +#define _XTENSA_PARAM_H + +#include + +#ifdef __KERNEL__ +# define HZ 100 /* internal timer frequency */ +# define USER_HZ 100 /* for user interfaces in "ticks" */ +# define CLOCKS_PER_SEC (USER_HZ) /* frequnzy at which times() counts */ +#endif + +#define EXEC_PAGESIZE (1 << XCHAL_MMU_MIN_PTE_PAGE_SIZE) + +#ifndef NGROUPS +#define NGROUPS 32 +#endif + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +#define MAXHOSTNAMELEN 64 /* max length of hostname */ + +#endif /* _XTENSA_PARAM_H */ diff --git a/include/asm-xtensa/pci-bridge.h b/include/asm-xtensa/pci-bridge.h new file mode 100644 index 000000000000..00fcbd7c534a --- /dev/null +++ b/include/asm-xtensa/pci-bridge.h @@ -0,0 +1,88 @@ +/* + * include/asm-xtensa/pci-bridge.h + * + * 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) 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PCI_BRIDGE_H +#define _XTENSA_PCI_BRIDGE_H + +#ifdef __KERNEL__ + +struct device_node; +struct pci_controller; + +/* + * pciauto_bus_scan() enumerates the pci space. + */ + +extern int pciauto_bus_scan(struct pci_controller *, int); + +struct pci_space { + unsigned long start; + unsigned long end; + unsigned long base; +}; + +/* + * Structure of a PCI controller (host bridge) + */ + +struct pci_controller { + int index; /* used for pci_controller_num */ + struct pci_controller *next; + struct pci_bus *bus; + void *arch_data; + + int first_busno; + int last_busno; + + struct pci_ops *ops; + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; + + /* Currently, we limit ourselves to 1 IO range and 3 mem + * ranges since the common pci_bus structure can't handle more + */ + struct resource io_resource; + struct resource mem_resources[3]; + int mem_resource_count; + + /* Host bridge I/O and Memory space + * Used for BAR placement algorithms + */ + struct pci_space io_space; + struct pci_space mem_space; + + /* Return the interrupt number fo a device. */ + int (*map_irq)(struct pci_dev*, u8, u8); + +}; + +static inline void pcibios_init_resource(struct resource *res, + unsigned long start, unsigned long end, int flags, char *name) +{ + res->start = start; + res->end = end; + res->flags = flags; + res->name = name; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; +} + + +/* These are used for config access before all the PCI probing has been done. */ +int early_read_config_byte(struct pci_controller*, int, int, int, u8*); +int early_read_config_word(struct pci_controller*, int, int, int, u16*); +int early_read_config_dword(struct pci_controller*, int, int, int, u32*); +int early_write_config_byte(struct pci_controller*, int, int, int, u8); +int early_write_config_word(struct pci_controller*, int, int, int, u16); +int early_write_config_dword(struct pci_controller*, int, int, int, u32); + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_PCI_BRIDGE_H */ diff --git a/include/asm-xtensa/pci.h b/include/asm-xtensa/pci.h new file mode 100644 index 000000000000..6817742301c2 --- /dev/null +++ b/include/asm-xtensa/pci.h @@ -0,0 +1,89 @@ +/* + * linux/include/asm-xtensa/pci.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PCI_H +#define _XTENSA_PCI_H + +#ifdef __KERNEL__ + +/* Can be used to override the logic in pci_scan_bus for skipping + * already-configured bus numbers - to be used for buggy BIOSes + * or architectures with incomplete PCI setup by the loader + */ + +#define pcibios_assign_all_busses() 0 + +extern struct pci_controller* pcibios_alloc_controller(void); + +extern inline void pcibios_set_master(struct pci_dev *dev) +{ + /* No special bus mastering setup handling */ +} + +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + +/* Assume some values. (We should revise them, if necessary) */ + +#define PCIBIOS_MIN_IO 0x2000 +#define PCIBIOS_MIN_MEM 0x10000000 + +/* Dynamic DMA mapping stuff. + * Xtensa has everything mapped statically like x86. + */ + +#include +#include +#include +#include +#include + +struct pci_dev; + +/* The PCI address space does equal the physical memory address space. + * The networking and block device layers use this boolean for bounce buffer + * decisions. + */ + +#define PCI_DMA_BUS_IS_PHYS (1) + +/* pci_unmap_{page,single} is a no-op, so */ +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) +#define pci_unmap_addr(PTR, ADDR_NAME) (0) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) +#define pci_ubnmap_len(PTR, LEN_NAME) (0) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) + +/* We cannot access memory above 4GB */ +#define pci_dac_dma_supported(pci_dev, mask) (0) + +/* Map a range of PCI memory or I/O space for a device into user space */ +int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); + +/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ +#define HAVE_PCI_MMAP 1 + +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + +#endif /* __KERNEL__ */ + +/* Implement the pci_ DMA API in terms of the generic device dma_ one */ +#include + +/* Generic PCI */ +#include + +#endif /* _XTENSA_PCI_H */ diff --git a/include/asm-xtensa/percpu.h b/include/asm-xtensa/percpu.h new file mode 100644 index 000000000000..6d2bc2ada9d1 --- /dev/null +++ b/include/asm-xtensa/percpu.h @@ -0,0 +1,16 @@ +/* + * linux/include/asm-xtensa/percpu.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PERCPU__ +#define _XTENSA_PERCPU__ + +#include + +#endif /* _XTENSA_PERCPU__ */ diff --git a/include/asm-xtensa/pgalloc.h b/include/asm-xtensa/pgalloc.h new file mode 100644 index 000000000000..734a8d060395 --- /dev/null +++ b/include/asm-xtensa/pgalloc.h @@ -0,0 +1,116 @@ +/* + * linux/include/asm-xtensa/pgalloc.h + * + * 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. + * + * Copyright (C) 2001-2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PGALLOC_H +#define _XTENSA_PGALLOC_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include + + +/* Cache aliasing: + * + * If the cache size for one way is greater than the page size, we have to + * deal with cache aliasing. The cache index is wider than the page size: + * + * |cache | + * |pgnum |page| virtual address + * |xxxxxX|zzzz| + * | | | + * \ / | | + * trans.| | + * / \ | | + * |yyyyyY|zzzz| physical address + * + * When the page number is translated to the physical page address, the lowest + * bit(s) (X) that are also part of the cache index are also translated (Y). + * If this translation changes this bit (X), the cache index is also afected, + * thus resulting in a different cache line than before. + * The kernel does not provide a mechanism to ensure that the page color + * (represented by this bit) remains the same when allocated or when pages + * are remapped. When user pages are mapped into kernel space, the color of + * the page might also change. + * + * We use the address space VMALLOC_END ... VMALLOC_END + DCACHE_WAY_SIZE * 2 + * to temporarily map a patch so we can match the color. + */ + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) +# define PAGE_COLOR_MASK (PAGE_MASK & (DCACHE_WAY_SIZE-1)) +# define PAGE_COLOR(a) \ + (((unsigned long)(a)&PAGE_COLOR_MASK) >> PAGE_SHIFT) +# define PAGE_COLOR_EQ(a,b) \ + ((((unsigned long)(a) ^ (unsigned long)(b)) & PAGE_COLOR_MASK) == 0) +# define PAGE_COLOR_MAP0(v) \ + (VMALLOC_END + ((unsigned long)(v) & PAGE_COLOR_MASK)) +# define PAGE_COLOR_MAP1(v) \ + (VMALLOC_END + ((unsigned long)(v) & PAGE_COLOR_MASK) + DCACHE_WAY_SIZE) +#endif + +/* + * Allocating and freeing a pmd is trivial: the 1-entry pmd is + * inside the pgd, so has no extra memory associated with it. + */ + +#define pgd_free(pgd) free_page((unsigned long)(pgd)) + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK + +static inline void +pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *pte) +{ + pmd_val(*(pmdp)) = (unsigned long)(pte); + __asm__ __volatile__ ("memw; dhwb %0, 0; dsync" :: "a" (pmdp)); +} + +static inline void +pmd_populate(struct mm_struct *mm, pmd_t *pmdp, struct page *page) +{ + pmd_val(*(pmdp)) = (unsigned long)page_to_virt(page); + __asm__ __volatile__ ("memw; dhwb %0, 0; dsync" :: "a" (pmdp)); +} + + + +#else + +# define pmd_populate_kernel(mm, pmdp, pte) \ + (pmd_val(*(pmdp)) = (unsigned long)(pte)) +# define pmd_populate(mm, pmdp, page) \ + (pmd_val(*(pmdp)) = (unsigned long)page_to_virt(page)) + +#endif + +static inline pgd_t* +pgd_alloc(struct mm_struct *mm) +{ + pgd_t *pgd; + + pgd = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, PGD_ORDER); + + if (likely(pgd != NULL)) + __flush_dcache_page((unsigned long)pgd); + + return pgd; +} + +extern pte_t* pte_alloc_one_kernel(struct mm_struct* mm, unsigned long addr); +extern struct page* pte_alloc_one(struct mm_struct* mm, unsigned long addr); + +#define pte_free_kernel(pte) free_page((unsigned long)pte) +#define pte_free(pte) __free_page(pte) + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_PGALLOC_H */ diff --git a/include/asm-xtensa/pgtable.h b/include/asm-xtensa/pgtable.h new file mode 100644 index 000000000000..0bb6416ae266 --- /dev/null +++ b/include/asm-xtensa/pgtable.h @@ -0,0 +1,468 @@ +/* + * linux/include/asm-xtensa/page.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version2 as + * published by the Free Software Foundation. + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PGTABLE_H +#define _XTENSA_PGTABLE_H + +#include +#include + +/* Assertions. */ + +#ifdef CONFIG_MMU + + +#if (XCHAL_MMU_RINGS < 2) +# error Linux build assumes at least 2 ring levels. +#endif + +#if (XCHAL_MMU_CA_BITS != 4) +# error We assume exactly four bits for CA. +#endif + +#if (XCHAL_MMU_SR_BITS != 0) +# error We have no room for SR bits. +#endif + +/* + * Use the first min-wired way for mapping page-table pages. + * Page coloring requires a second min-wired way. + */ + +#if (XCHAL_DTLB_MINWIRED_SETS == 0) +# error Need a min-wired way for mapping page-table pages +#endif + +#define DTLB_WAY_PGTABLE XCHAL_DTLB_SET(XCHAL_DTLB_MINWIRED_SET0, WAY) + +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK +# if XCHAL_DTLB_SET(XCHAL_DTLB_MINWIRED_SET0, WAYS) >= 2 +# define DTLB_WAY_DCACHE_ALIAS0 (DTLB_WAY_PGTABLE + 1) +# define DTLB_WAY_DCACHE_ALIAS1 (DTLB_WAY_PGTABLE + 2) +# else +# error Page coloring requires its own wired dtlb way! +# endif +#endif + +#endif /* CONFIG_MMU */ + +/* + * We only use two ring levels, user and kernel space. + */ + +#define USER_RING 1 /* user ring level */ +#define KERNEL_RING 0 /* kernel ring level */ + +/* + * The Xtensa architecture port of Linux has a two-level page table system, + * i.e. the logical three-level Linux page table layout are folded. + * Each task has the following memory page tables: + * + * PGD table (page directory), ie. 3rd-level page table: + * One page (4 kB) of 1024 (PTRS_PER_PGD) pointers to PTE tables + * (Architectures that don't have the PMD folded point to the PMD tables) + * + * The pointer to the PGD table for a given task can be retrieved from + * the task structure (struct task_struct*) t, e.g. current(): + * (t->mm ? t->mm : t->active_mm)->pgd + * + * PMD tables (page middle-directory), ie. 2nd-level page tables: + * Absent for the Xtensa architecture (folded, PTRS_PER_PMD == 1). + * + * PTE tables (page table entry), ie. 1st-level page tables: + * One page (4 kB) of 1024 (PTRS_PER_PTE) PTEs with a special PTE + * invalid_pte_table for absent mappings. + * + * The individual pages are 4 kB big with special pages for the empty_zero_page. + */ +#define PGDIR_SHIFT 22 +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +/* + * Entries per page directory level: we use two-level, so + * we don't really have any PMD directory physically. + */ +#define PTRS_PER_PTE 1024 +#define PTRS_PER_PTE_SHIFT 10 +#define PTRS_PER_PMD 1 +#define PTRS_PER_PGD 1024 +#define PGD_ORDER 0 +#define PMD_ORDER 0 +#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) +#define FIRST_USER_ADDRESS XCHAL_SEG_MAPPABLE_VADDR +#define FIRST_USER_PGD_NR (FIRST_USER_ADDRESS >> PGDIR_SHIFT) + +/* virtual memory area. We keep a distance to other memory regions to be + * on the safe side. We also use this area for cache aliasing. + */ + +// FIXME: virtual memory area must be configuration-dependent + +#define VMALLOC_START 0xC0000000 +#define VMALLOC_END 0xC7FF0000 + +/* Xtensa Linux config PTE layout (when present): + * 31-12: PPN + * 11-6: Software + * 5-4: RING + * 3-0: CA + * + * Similar to the Alpha and MIPS ports, we need to keep track of the ref + * and mod bits in software. We have a software "you can read + * from this page" bit, and a hardware one which actually lets the + * process read from the page. On the same token we have a software + * writable bit and the real hardware one which actually lets the + * process write to the page. + * + * See further below for PTE layout for swapped-out pages. + */ + +#define _PAGE_VALID (1<<0) /* hardware: page is accessible */ +#define _PAGE_WRENABLE (1<<1) /* hardware: page is writable */ + +/* None of these cache modes include MP coherency: */ +#define _PAGE_NO_CACHE (0<<2) /* bypass, non-speculative */ +#if XCHAL_DCACHE_IS_WRITEBACK +# define _PAGE_WRITEBACK (1<<2) /* write back */ +# define _PAGE_WRITETHRU (2<<2) /* write through */ +#else +# define _PAGE_WRITEBACK (1<<2) /* assume write through */ +# define _PAGE_WRITETHRU (1<<2) +#endif +#define _PAGE_NOALLOC (3<<2) /* don't allocate cache,if not cached */ +#define _CACHE_MASK (3<<2) + +#define _PAGE_USER (1<<4) /* user access (ring=1) */ +#define _PAGE_KERNEL (0<<4) /* kernel access (ring=0) */ + +/* Software */ +#define _PAGE_RW (1<<6) /* software: page writable */ +#define _PAGE_DIRTY (1<<7) /* software: page dirty */ +#define _PAGE_ACCESSED (1<<8) /* software: page accessed (read) */ +#define _PAGE_FILE (1<<9) /* nonlinear file mapping*/ + +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _CACHE_MASK | _PAGE_DIRTY) +#define _PAGE_PRESENT ( _PAGE_VALID | _PAGE_WRITEBACK | _PAGE_ACCESSED) + +#ifdef CONFIG_MMU + +# define PAGE_NONE __pgprot(_PAGE_PRESENT) +# define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_RW) +# define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER) +# define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER) +# define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_KERNEL | _PAGE_WRENABLE) +# define PAGE_INVALID __pgprot(_PAGE_USER) + +# if (DCACHE_WAY_SIZE > PAGE_SIZE) +# define PAGE_DIRECTORY __pgprot(_PAGE_VALID | _PAGE_ACCESSED | _PAGE_KERNEL) +# else +# define PAGE_DIRECTORY __pgprot(_PAGE_PRESENT | _PAGE_KERNEL) +# endif + +#else /* no mmu */ + +# define PAGE_NONE __pgprot(0) +# define PAGE_SHARED __pgprot(0) +# define PAGE_COPY __pgprot(0) +# define PAGE_READONLY __pgprot(0) +# define PAGE_KERNEL __pgprot(0) + +#endif + +/* + * On certain configurations of Xtensa MMUs (eg. the initial Linux config), + * the MMU can't do page protection for execute, and considers that the same as + * read. Also, write permissions may imply read permissions. + * What follows is the closest we can get by reasonable means.. + * See linux/mm/mmap.c for protection_map[] array that uses these definitions. + */ +#define __P000 PAGE_NONE /* private --- */ +#define __P001 PAGE_READONLY /* private --r */ +#define __P010 PAGE_COPY /* private -w- */ +#define __P011 PAGE_COPY /* private -wr */ +#define __P100 PAGE_READONLY /* private x-- */ +#define __P101 PAGE_READONLY /* private x-r */ +#define __P110 PAGE_COPY /* private xw- */ +#define __P111 PAGE_COPY /* private xwr */ + +#define __S000 PAGE_NONE /* shared --- */ +#define __S001 PAGE_READONLY /* shared --r */ +#define __S010 PAGE_SHARED /* shared -w- */ +#define __S011 PAGE_SHARED /* shared -wr */ +#define __S100 PAGE_READONLY /* shared x-- */ +#define __S101 PAGE_READONLY /* shared x-r */ +#define __S110 PAGE_SHARED /* shared xw- */ +#define __S111 PAGE_SHARED /* shared xwr */ + +#ifndef __ASSEMBLY__ + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd entry %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +extern unsigned long empty_zero_page[1024]; + +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +extern pgd_t swapper_pg_dir[PAGE_SIZE/sizeof(pgd_t)]; + +/* + * The pmd contains the kernel virtual address of the pte page. + */ +#define pmd_page_kernel(pmd) ((unsigned long)(pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd) virt_to_page(pmd_val(pmd)) + +/* + * The following only work if pte_present() is true. + */ +#define pte_none(pte) (!(pte_val(pte) ^ _PAGE_USER)) +#define pte_present(pte) (pte_val(pte) & _PAGE_VALID) +#define pte_clear(mm,addr,ptep) \ + do { update_pte(ptep, __pte(_PAGE_USER)); } while(0) + +#define pmd_none(pmd) (!pmd_val(pmd)) +#define pmd_present(pmd) (pmd_val(pmd) & PAGE_MASK) +#define pmd_clear(pmdp) do { set_pmd(pmdp, __pmd(0)); } while (0) +#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK) + +/* Note: We use the _PAGE_USER bit to indicate write-protect kernel memory */ + +static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER; } +static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW; } +static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } +static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; } +static inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~(_PAGE_RW | _PAGE_WRENABLE); return pte; } +static inline pte_t pte_rdprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_USER; return pte; } +static inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; } +static inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkread(pte_t pte) { pte_val(pte) |= _PAGE_USER; return pte; } +static inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; } +static inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_RW; return pte; } + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT) +#define pte_same(a,b) (pte_val(a) == pte_val(b)) +#define pte_page(x) pfn_to_page(pte_pfn(x)) +#define pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) +#define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot) + +extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot)); +} + +/* + * Certain architectures need to do special things when pte's + * within a page table are directly modified. Thus, the following + * hook is made available. + */ +static inline void update_pte(pte_t *ptep, pte_t pteval) +{ + *ptep = pteval; +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK + __asm__ __volatile__ ("memw; dhwb %0, 0; dsync" :: "a" (ptep)); +#endif +} + +extern inline void +set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pteval) +{ + update_pte(ptep, pteval); +} + + +extern inline void +set_pmd(pmd_t *pmdp, pmd_t pmdval) +{ + *pmdp = pmdval; +#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK + __asm__ __volatile__ ("memw; dhwb %0, 0; dsync" :: "a" (pmdp)); +#endif +} + + +static inline int +ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + pte_t pte = *ptep; + if (!pte_young(pte)) + return 0; + update_pte(ptep, pte_mkold(pte)); + return 1; +} + +static inline int +ptep_test_and_clear_dirty(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + pte_t pte = *ptep; + if (!pte_dirty(pte)) + return 0; + update_pte(ptep, pte_mkclean(pte)); + return 1; +} + +static inline pte_t +ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + pte_t pte = *ptep; + pte_clear(mm, addr, ptep); + return pte; +} + +static inline void +ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + pte_t pte = *ptep; + update_pte(ptep, pte_wrprotect(pte)); +} + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +/* to find an entry in a page-table-directory */ +#define pgd_offset(mm,address) ((mm)->pgd + pgd_index(address)) + +#define pgd_index(address) ((address) >> PGDIR_SHIFT) + +/* Find an entry in the second-level page table.. */ +#define pmd_offset(dir,address) ((pmd_t*)(dir)) + +/* Find an entry in the third-level page table.. */ +#define pte_index(address) (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_offset_kernel(dir,addr) \ + ((pte_t*) pmd_page_kernel(*(dir)) + pte_index(addr)) +#define pte_offset_map(dir,addr) pte_offset_kernel((dir),(addr)) +#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir),(addr)) + +#define pte_unmap(pte) do { } while (0) +#define pte_unmap_nested(pte) do { } while (0) + + +/* + * Encode and decode a swap entry. + * Each PTE in a process VM's page table is either: + * "present" -- valid and not swapped out, protection bits are meaningful; + * "not present" -- which further subdivides in these two cases: + * "none" -- no mapping at all; identified by pte_none(), set by pte_clear( + * "swapped out" -- the page is swapped out, and the SWP macros below + * are used to store swap file info in the PTE itself. + * + * In the Xtensa processor MMU, any PTE entries in user space (or anywhere + * in virtual memory that can map differently across address spaces) + * must have a correct ring value that represents the RASID field that + * is changed when switching address spaces. Eg. such PTE entries cannot + * be set to ring zero, because that can cause a (global) kernel ASID + * entry to be created in the TLBs (even with invalid cache attribute), + * potentially causing a multihit exception when going back to another + * address space that mapped the same virtual address at another ring. + * + * SO: we avoid using ring bits (_PAGE_RING_MASK) in "not present" PTEs. + * We also avoid using the _PAGE_VALID bit which must be zero for non-present + * pages. + * + * We end up with the following available bits: 1..3 and 7..31. + * We don't bother with 1..3 for now (we can use them later if needed), + * and chose to allocate 6 bits for SWP_TYPE and the remaining 19 bits + * for SWP_OFFSET. At least 5 bits are needed for SWP_TYPE, because it + * is currently implemented as an index into swap_info[MAX_SWAPFILES] + * and MAX_SWAPFILES is currently defined as 32 in . + * However, for some reason all other architectures in the 2.4 kernel + * reserve either 6, 7, or 8 bits so I'll not detract from that for now. :) + * SWP_OFFSET is an offset into the swap file in page-size units, so + * with 4 kB pages, 19 bits supports a maximum swap file size of 2 GB. + * + * FIXME: 2 GB isn't very big. Other bits can be used to allow + * larger swap sizes. In the meantime, it appears relatively easy to get + * around the 2 GB limitation by simply using multiple swap files. + */ + +#define __swp_type(entry) (((entry).val >> 7) & 0x3f) +#define __swp_offset(entry) ((entry).val >> 13) +#define __swp_entry(type,offs) ((swp_entry_t) {((type) << 7) | ((offs) << 13)}) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + +#define PTE_FILE_MAX_BITS 29 +#define pte_to_pgoff(pte) (pte_val(pte) >> 3) +#define pgoff_to_pte(off) ((pte_t) { ((off) << 3) | _PAGE_FILE }) + + +#endif /* !defined (__ASSEMBLY__) */ + + +#ifdef __ASSEMBLY__ + +/* Assembly macro _PGD_INDEX is the same as C pgd_index(unsigned long), + * _PGD_OFFSET as C pgd_offset(struct mm_struct*, unsigned long), + * _PMD_OFFSET as C pmd_offset(pgd_t*, unsigned long) + * _PTE_OFFSET as C pte_offset(pmd_t*, unsigned long) + * + * Note: We require an additional temporary register which can be the same as + * the register that holds the address. + * + * ((pte_t*) ((unsigned long)(pmd_val(*pmd) & PAGE_MASK)) + pte_index(addr)) + * + */ +#define _PGD_INDEX(rt,rs) extui rt, rs, PGDIR_SHIFT, 32-PGDIR_SHIFT +#define _PTE_INDEX(rt,rs) extui rt, rs, PAGE_SHIFT, PTRS_PER_PTE_SHIFT + +#define _PGD_OFFSET(mm,adr,tmp) l32i mm, mm, MM_PGD; \ + _PGD_INDEX(tmp, adr); \ + addx4 mm, tmp, mm + +#define _PTE_OFFSET(pmd,adr,tmp) _PTE_INDEX(tmp, adr); \ + srli pmd, pmd, PAGE_SHIFT; \ + slli pmd, pmd, PAGE_SHIFT; \ + addx4 pmd, tmp, pmd + +#else + +extern void paging_init(void); + +#define kern_addr_valid(addr) (1) + +extern void update_mmu_cache(struct vm_area_struct * vma, + unsigned long address, pte_t pte); + +/* + * remap a physical address `phys' of size `size' with page protection `prot' + * into virtual address `from' + */ +#define io_remap_page_range(vma,from,phys,size,prot) \ + remap_pfn_range(vma, from, (phys) >> PAGE_SHIFT, size, prot) + + +/* No page table caches to init */ + +#define pgtable_cache_init() do { } while (0) + +typedef pte_t *pte_addr_t; + +#endif /* !defined (__ASSEMBLY__) */ + +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY +#define __HAVE_ARCH_PTEP_GET_AND_CLEAR +#define __HAVE_ARCH_PTEP_SET_WRPROTECT +#define __HAVE_ARCH_PTEP_MKDIRTY +#define __HAVE_ARCH_PTE_SAME + +#include + +#endif /* _XTENSA_PGTABLE_H */ diff --git a/include/asm-xtensa/platform-iss/hardware.h b/include/asm-xtensa/platform-iss/hardware.h new file mode 100644 index 000000000000..22240f001803 --- /dev/null +++ b/include/asm-xtensa/platform-iss/hardware.h @@ -0,0 +1,29 @@ +/* + * include/asm-xtensa/platform-iss/hardware.h + * + * 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) 2001 Tensilica Inc. + */ + +/* + * This file contains the default configuration of ISS. + */ + +#ifndef __ASM_XTENSA_ISS_HARDWARE +#define __ASM_XTENSA_ISS_HARDWARE + +/* + * Memory configuration. + */ + +#define PLATFORM_DEFAULT_MEM_START XSHAL_RAM_PADDR +#define PLATFORM_DEFAULT_MEM_SIZE XSHAL_RAM_VSIZE + +/* + * Interrupt configuration. + */ + +#endif /* __ASM_XTENSA_ISS_HARDWARE */ diff --git a/include/asm-xtensa/platform.h b/include/asm-xtensa/platform.h new file mode 100644 index 000000000000..36163894bc20 --- /dev/null +++ b/include/asm-xtensa/platform.h @@ -0,0 +1,92 @@ +/* + * include/asm-xtensa/platform.h + * + * Platform specific functions + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PLATFORM_H +#define _XTENSA_PLATFORM_H + +#include +#include +#include + +#include + +/* + * platform_init is called before the mmu is initialized to give the + * platform a early hook-up. bp_tag_t is a list of configuration tags + * passed from the boot-loader. + */ +extern void platform_init(bp_tag_t*); + +/* + * platform_setup is called from setup_arch with a pointer to the command-line + * string. + */ +extern void platform_setup (char **); + +/* + * platform_init_irq is called from init_IRQ. + */ +extern void platform_init_irq (void); + +/* + * platform_restart is called to restart the system. + */ +extern void platform_restart (void); + +/* + * platform_halt is called to stop the system and halt. + */ +extern void platform_halt (void); + +/* + * platform_power_off is called to stop the system and power it off. + */ +extern void platform_power_off (void); + +/* + * platform_idle is called from the idle function. + */ +extern void platform_idle (void); + +/* + * platform_heartbeat is called every HZ + */ +extern void platform_heartbeat (void); + +/* + * platform_pcibios_init is called to allow the platform to setup the pci bus. + */ +extern void platform_pcibios_init (void); + +/* + * platform_pcibios_fixup allows to modify the PCI configuration. + */ +extern int platform_pcibios_fixup (void); + +/* + * platform_calibrate_ccount calibrates cpu clock freq (CONFIG_XTENSA_CALIBRATE) + */ +extern void platform_calibrate_ccount (void); + +/* + * platform_get_rtc_time returns RTC seconds (returns 0 for no error) + */ +extern int platform_get_rtc_time(time_t*); + +/* + * platform_set_rtc_time set RTC seconds (returns 0 for no error) + */ +extern int platform_set_rtc_time(time_t); + + +#endif /* _XTENSA_PLATFORM_H */ + diff --git a/include/asm-xtensa/poll.h b/include/asm-xtensa/poll.h new file mode 100644 index 000000000000..dffe447534e0 --- /dev/null +++ b/include/asm-xtensa/poll.h @@ -0,0 +1,37 @@ +/* + * include/asm-xtensa/poll.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_POLL_H +#define _XTENSA_POLL_H + + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 + +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM POLLOUT +#define POLLWRBAND 0x0100 + +#define POLLMSG 0x0400 +#define POLLREMOVE 0x0800 + +struct pollfd { + int fd; + short events; + short revents; +}; + +#endif /* _XTENSA_POLL_H */ diff --git a/include/asm-xtensa/posix_types.h b/include/asm-xtensa/posix_types.h new file mode 100644 index 000000000000..2c816b0e7762 --- /dev/null +++ b/include/asm-xtensa/posix_types.h @@ -0,0 +1,123 @@ +/* + * include/asm-xtensa/posix_types.h + * + * 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. + * + * Largely copied from include/asm-ppc/posix_types.h + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_POSIX_TYPES_H +#define _XTENSA_POSIX_TYPES_H + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + */ + +typedef unsigned long __kernel_ino_t; +typedef unsigned int __kernel_mode_t; +typedef unsigned short __kernel_nlink_t; +typedef long __kernel_off_t; +typedef int __kernel_pid_t; +typedef unsigned short __kernel_ipc_pid_t; +typedef unsigned int __kernel_uid_t; +typedef unsigned int __kernel_gid_t; +typedef unsigned int __kernel_size_t; +typedef int __kernel_ssize_t; +typedef long __kernel_ptrdiff_t; +typedef long __kernel_time_t; +typedef long __kernel_suseconds_t; +typedef long __kernel_clock_t; +typedef int __kernel_timer_t; +typedef int __kernel_clockid_t; +typedef int __kernel_daddr_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; +typedef unsigned short __kernel_old_dev_t; + +#ifdef __GNUC__ +typedef long long __kernel_loff_t; +#endif + +typedef struct { + int val[2]; +} __kernel_fsid_t; + +#ifndef __GNUC__ + +#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d)) +#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d)) +#define __FD_ISSET(d, set) ((set)->fds_bits[__FDELT(d)] & __FDMASK(d)) +#define __FD_ZERO(set) \ + ((void) memset ((__ptr_t) (set), 0, sizeof (__kernel_fd_set))) + +#else /* __GNUC__ */ + +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) \ + || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0) +/* With GNU C, use inline functions instead so args are evaluated only once: */ + +#undef __FD_SET +static __inline__ void __FD_SET(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] |= (1UL<<_rem); +} + +#undef __FD_CLR +static __inline__ void __FD_CLR(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] &= ~(1UL<<_rem); +} + +#undef __FD_ISSET +static __inline__ int __FD_ISSET(unsigned long fd, __kernel_fd_set *p) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + return (p->fds_bits[_tmp] & (1UL<<_rem)) != 0; +} + +/* + * This will unroll the loop for the normal constant case (8 ints, + * for a 256-bit fd_set) + */ +#undef __FD_ZERO +static __inline__ void __FD_ZERO(__kernel_fd_set *p) +{ + unsigned int *tmp = (unsigned int *)p->fds_bits; + int i; + + if (__builtin_constant_p(__FDSET_LONGS)) { + switch (__FDSET_LONGS) { + case 8: + tmp[0] = 0; tmp[1] = 0; tmp[2] = 0; tmp[3] = 0; + tmp[4] = 0; tmp[5] = 0; tmp[6] = 0; tmp[7] = 0; + return; + } + } + i = __FDSET_LONGS; + while (i) { + i--; + *tmp = 0; + tmp++; + } +} + +#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */ +#endif /* __GNUC__ */ +#endif /* _XTENSA_POSIX_TYPES_H */ diff --git a/include/asm-xtensa/processor.h b/include/asm-xtensa/processor.h new file mode 100644 index 000000000000..9cab5e4298b9 --- /dev/null +++ b/include/asm-xtensa/processor.h @@ -0,0 +1,205 @@ +/* + * include/asm-xtensa/processor.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PROCESSOR_H +#define _XTENSA_PROCESSOR_H + +#ifdef __ASSEMBLY__ +#define _ASMLANGUAGE +#endif + +#include +#include +#include +#include + +#include +#include +#include + +/* Assertions. */ + +#if (XCHAL_HAVE_WINDOWED != 1) +#error Linux requires the Xtensa Windowed Registers Option. +#endif + +/* + * User space process size: 1 GB. + * Windowed call ABI requires caller and callee to be located within the same + * 1 GB region. The C compiler places trampoline code on the stack for sources + * that take the address of a nested C function (a feature used by glibc), so + * the 1 GB requirement applies to the stack as well. + */ + +#define TASK_SIZE 0x40000000 + +/* + * General exception cause assigned to debug exceptions. Debug exceptions go + * to their own vector, rather than the general exception vectors (user, + * kernel, double); and their specific causes are reported via DEBUGCAUSE + * rather than EXCCAUSE. However it is sometimes convenient to redirect debug + * exceptions to the general exception mechanism. To do this, an otherwise + * unused EXCCAUSE value was assigned to debug exceptions for this purpose. + */ + +#define EXCCAUSE_MAPPED_DEBUG 63 + +/* + * We use DEPC also as a flag to distinguish between double and regular + * exceptions. For performance reasons, DEPC might contain the value of + * EXCCAUSE for regular exceptions, so we use this definition to mark a + * valid double exception address. + * (Note: We use it in bgeui, so it should be 64, 128, or 256) + */ + +#define VALID_DOUBLE_EXCEPTION_ADDRESS 64 + +/* LOCKLEVEL defines the interrupt level that masks all + * general-purpose interrupts. + */ +#define LOCKLEVEL 1 + +/* WSBITS and WBBITS are the width of the WINDOWSTART and WINDOWBASE + * registers + */ +#define WSBITS (XCHAL_NUM_AREGS / 4) /* width of WINDOWSTART in bits */ +#define WBBITS (XCHAL_NUM_AREGS_LOG2 - 2) /* width of WINDOWBASE in bits */ + +#ifndef __ASSEMBLY__ + +/* Build a valid return address for the specified call winsize. + * winsize must be 1 (call4), 2 (call8), or 3 (call12) + */ +#define MAKE_RA_FOR_CALL(ra,ws) (((ra) & 0x3fffffff) | (ws) << 30) + +/* Convert return address to a valid pc + * Note: We assume that the stack pointer is in the same 1GB ranges as the ra + */ +#define MAKE_PC_FROM_RA(ra,sp) (((ra) & 0x3fffffff) | ((sp) & 0xc0000000)) + +typedef struct { + unsigned long seg; +} mm_segment_t; + +struct thread_struct { + + /* kernel's return address and stack pointer for context switching */ + unsigned long ra; /* kernel's a0: return address and window call size */ + unsigned long sp; /* kernel's a1: stack pointer */ + + mm_segment_t current_ds; /* see uaccess.h for example uses */ + + /* struct xtensa_cpuinfo info; */ + + unsigned long bad_vaddr; /* last user fault */ + unsigned long bad_uaddr; /* last kernel fault accessing user space */ + unsigned long error_code; + + unsigned long ibreak[XCHAL_NUM_IBREAK]; + unsigned long dbreaka[XCHAL_NUM_DBREAK]; + unsigned long dbreakc[XCHAL_NUM_DBREAK]; + + /* Allocate storage for extra state and coprocessor state. */ + unsigned char cp_save[XTENSA_CP_EXTRA_SIZE] + __attribute__ ((aligned(XTENSA_CP_EXTRA_ALIGN))); + + /* Make structure 16 bytes aligned. */ + int align[0] __attribute__ ((aligned(16))); +}; + + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l;}) + + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE (TASK_SIZE / 2) + +#define INIT_THREAD \ +{ \ + ra: 0, \ + sp: sizeof(init_stack) + (long) &init_stack, \ + current_ds: {0}, \ + /*info: {0}, */ \ + bad_vaddr: 0, \ + bad_uaddr: 0, \ + error_code: 0, \ +} + + +/* + * Do necessary setup to start up a newly executed thread. + * Note: We set-up ps as if we did a call4 to the new pc. + * set_thread_state in signal.c depends on it. + */ +#define USER_PS_VALUE ( (1 << XCHAL_PS_WOE_SHIFT) + \ + (1 << XCHAL_PS_CALLINC_SHIFT) + \ + (USER_RING << XCHAL_PS_RING_SHIFT) + \ + (1 << XCHAL_PS_PROGSTACK_SHIFT) + \ + (1 << XCHAL_PS_EXCM_SHIFT) ) + +/* Clearing a0 terminates the backtrace. */ +#define start_thread(regs, new_pc, new_sp) \ + regs->pc = new_pc; \ + regs->ps = USER_PS_VALUE; \ + regs->areg[1] = new_sp; \ + regs->areg[0] = 0; \ + regs->wmask = 1; \ + regs->depc = 0; \ + regs->windowbase = 0; \ + regs->windowstart = 1; + +/* Forward declaration */ +struct task_struct; +struct mm_struct; + +// FIXME: do we need release_thread for CP?? +/* Free all resources held by a thread. */ +#define release_thread(thread) do { } while(0) + +// FIXME: do we need prepare_to_copy (lazy status) for CP?? +/* Prepare to copy thread state - unlazy all lazy status */ +#define prepare_to_copy(tsk) do { } while (0) + +/* + * create a kernel thread without removing it from tasklists + */ +extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); + +/* Copy and release all segment info associated with a VM */ + +#define copy_segments(p, mm) do { } while(0) +#define release_segments(mm) do { } while(0) +#define forget_segments() do { } while (0) + +#define thread_saved_pc(tsk) (xtensa_pt_regs(tsk)->pc) + +extern unsigned long get_wchan(struct task_struct *p); + +#define KSTK_EIP(tsk) (xtensa_pt_regs(tsk)->pc) +#define KSTK_ESP(tsk) (xtensa_pt_regs(tsk)->areg[1]) + +#define cpu_relax() do { } while (0) + +/* Special register access. */ + +#define WSR(v,sr) __asm__ __volatile__ ("wsr %0,"__stringify(sr) :: "a"(v)); +#define RSR(v,sr) __asm__ __volatile__ ("rsr %0,"__stringify(sr) : "=a"(v)); + +#define set_sr(x,sr) ({unsigned int v=(unsigned int)x; WSR(v,sr);}) +#define get_sr(sr) ({unsigned int v; RSR(v,sr); v; }) + +#endif /* __ASSEMBLY__ */ +#endif /* _XTENSA_PROCESSOR_H */ diff --git a/include/asm-xtensa/ptrace.h b/include/asm-xtensa/ptrace.h new file mode 100644 index 000000000000..2848a5ff8349 --- /dev/null +++ b/include/asm-xtensa/ptrace.h @@ -0,0 +1,135 @@ +/* + * include/asm-xtensa/ptrace.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_PTRACE_H +#define _XTENSA_PTRACE_H + +#include + +/* + * Kernel stack + * + * +-----------------------+ -------- STACK_SIZE + * | register file | | + * +-----------------------+ | + * | struct pt_regs | | + * +-----------------------+ | ------ PT_REGS_OFFSET + * double : 16 bytes spill area : | ^ + * excetion :- - - - - - - - - - - -: | | + * frame : struct pt_regs : | | + * :- - - - - - - - - - - -: | | + * | | | | + * | memory stack | | | + * | | | | + * ~ ~ ~ ~ + * ~ ~ ~ ~ + * | | | | + * | | | | + * +-----------------------+ | | --- STACK_BIAS + * | struct task_struct | | | ^ + * current --> +-----------------------+ | | | + * | struct thread_info | | | | + * +-----------------------+ -------- + */ + +#define KERNEL_STACK_SIZE (2 * PAGE_SIZE) + +/* Offsets for exception_handlers[] (3 x 64-entries x 4-byte tables). */ + +#define EXC_TABLE_KSTK 0x004 /* Kernel Stack */ +#define EXC_TABLE_DOUBLE_SAVE 0x008 /* Double exception save area for a0 */ +#define EXC_TABLE_FIXUP 0x00c /* Fixup handler */ +#define EXC_TABLE_PARAM 0x010 /* For passing a parameter to fixup */ +#define EXC_TABLE_SYSCALL_SAVE 0x014 /* For fast syscall handler */ +#define EXC_TABLE_FAST_USER 0x100 /* Fast user exception handler */ +#define EXC_TABLE_FAST_KERNEL 0x200 /* Fast kernel exception handler */ +#define EXC_TABLE_DEFAULT 0x300 /* Default C-Handler */ +#define EXC_TABLE_SIZE 0x400 + +/* Registers used by strace */ + +#define REG_A_BASE 0xfc000000 +#define REG_AR_BASE 0x04000000 +#define REG_PC 0x14000000 +#define REG_PS 0x080000e6 +#define REG_WB 0x08000048 +#define REG_WS 0x08000049 +#define REG_LBEG 0x08000000 +#define REG_LEND 0x08000001 +#define REG_LCOUNT 0x08000002 +#define REG_SAR 0x08000003 +#define REG_DEPC 0x080000c0 +#define REG_EXCCAUSE 0x080000e8 +#define REG_EXCVADDR 0x080000ee +#define SYSCALL_NR 0x1 + +#define AR_REGNO_TO_A_REGNO(ar, wb) (ar - wb*4) & ~(XCHAL_NUM_AREGS - 1) + +/* Other PTRACE_ values defined in using values 0-9,16,17,24 */ + +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 +#define PTRACE_GETFPREGSIZE 18 + +#ifndef __ASSEMBLY__ + +/* + * This struct defines the way the registers are stored on the + * kernel stack during a system call or other kernel entry. + */ +struct pt_regs { + unsigned long pc; /* 4 */ + unsigned long ps; /* 8 */ + unsigned long depc; /* 12 */ + unsigned long exccause; /* 16 */ + unsigned long excvaddr; /* 20 */ + unsigned long debugcause; /* 24 */ + unsigned long wmask; /* 28 */ + unsigned long lbeg; /* 32 */ + unsigned long lend; /* 36 */ + unsigned long lcount; /* 40 */ + unsigned long sar; /* 44 */ + unsigned long windowbase; /* 48 */ + unsigned long windowstart; /* 52 */ + unsigned long syscall; /* 56 */ + int reserved[2]; /* 64 */ + + /* Make sure the areg field is 16 bytes aligned. */ + int align[0] __attribute__ ((aligned(16))); + + /* current register frame. + * Note: The ESF for kernel exceptions ends after 16 registers! + */ + unsigned long areg[16]; /* 128 (64) */ +}; + +#ifdef __KERNEL__ +# define xtensa_pt_regs(tsk) ((struct pt_regs*) \ + (((long)(tsk)->thread_info + KERNEL_STACK_SIZE - (XCHAL_NUM_AREGS-16)*4)) - 1) +# define user_mode(regs) (((regs)->ps & 0x00000020)!=0) +# define instruction_pointer(regs) ((regs)->pc) +extern void show_regs(struct pt_regs *); + +# ifndef CONFIG_SMP +# define profile_pc(regs) instruction_pointer(regs) +# endif +#endif /* __KERNEL__ */ + +#else /* __ASSEMBLY__ */ + +#ifdef __KERNEL__ +# include +#define PT_REGS_OFFSET (KERNEL_STACK_SIZE - PT_USER_SIZE) +#endif + +#endif /* !__ASSEMBLY__ */ +#endif /* _XTENSA_PTRACE_H */ diff --git a/include/asm-xtensa/resource.h b/include/asm-xtensa/resource.h new file mode 100644 index 000000000000..17b5ab311771 --- /dev/null +++ b/include/asm-xtensa/resource.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/resource.h + * + * 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) 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_RESOURCE_H +#define _XTENSA_RESOURCE_H + +#include + +#endif /* _XTENSA_RESOURCE_H */ diff --git a/include/asm-xtensa/rmap.h b/include/asm-xtensa/rmap.h new file mode 100644 index 000000000000..649588b7e9ad --- /dev/null +++ b/include/asm-xtensa/rmap.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/rmap.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_RMAP_H +#define _XTENSA_RMAP_H + +#include + +#endif diff --git a/include/asm-xtensa/rwsem.h b/include/asm-xtensa/rwsem.h new file mode 100644 index 000000000000..3c02b0e033f0 --- /dev/null +++ b/include/asm-xtensa/rwsem.h @@ -0,0 +1,175 @@ +/* + * include/asm-xtensa/rwsem.h + * + * 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. + * + * Largely copied from include/asm-ppc/rwsem.h + * + * Copyright (C) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_RWSEM_H +#define _XTENSA_RWSEM_H + +#include +#include +#include +#include + +/* + * the semaphore definition + */ +struct rw_semaphore { + signed long count; +#define RWSEM_UNLOCKED_VALUE 0x00000000 +#define RWSEM_ACTIVE_BIAS 0x00000001 +#define RWSEM_ACTIVE_MASK 0x0000ffff +#define RWSEM_WAITING_BIAS (-0x00010000) +#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS +#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) + spinlock_t wait_lock; + struct list_head wait_list; +#if RWSEM_DEBUG + int debug; +#endif +}; + +/* + * initialisation + */ +#if RWSEM_DEBUG +#define __RWSEM_DEBUG_INIT , 0 +#else +#define __RWSEM_DEBUG_INIT /* */ +#endif + +#define __RWSEM_INITIALIZER(name) \ + { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ + LIST_HEAD_INIT((name).wait_list) \ + __RWSEM_DEBUG_INIT } + +#define DECLARE_RWSEM(name) \ + struct rw_semaphore name = __RWSEM_INITIALIZER(name) + +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); + +static inline void init_rwsem(struct rw_semaphore *sem) +{ + sem->count = RWSEM_UNLOCKED_VALUE; + spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->wait_list); +#if RWSEM_DEBUG + sem->debug = 0; +#endif +} + +/* + * lock for reading + */ +static inline void __down_read(struct rw_semaphore *sem) +{ + if (atomic_add_return(1,(atomic_t *)(&sem->count)) > 0) + smp_wmb(); + else + rwsem_down_read_failed(sem); +} + +static inline int __down_read_trylock(struct rw_semaphore *sem) +{ + int tmp; + + while ((tmp = sem->count) >= 0) { + if (tmp == cmpxchg(&sem->count, tmp, + tmp + RWSEM_ACTIVE_READ_BIAS)) { + smp_wmb(); + return 1; + } + } + return 0; +} + +/* + * lock for writing + */ +static inline void __down_write(struct rw_semaphore *sem) +{ + int tmp; + + tmp = atomic_add_return(RWSEM_ACTIVE_WRITE_BIAS, + (atomic_t *)(&sem->count)); + if (tmp == RWSEM_ACTIVE_WRITE_BIAS) + smp_wmb(); + else + rwsem_down_write_failed(sem); +} + +static inline int __down_write_trylock(struct rw_semaphore *sem) +{ + int tmp; + + tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE, + RWSEM_ACTIVE_WRITE_BIAS); + smp_wmb(); + return tmp == RWSEM_UNLOCKED_VALUE; +} + +/* + * unlock after reading + */ +static inline void __up_read(struct rw_semaphore *sem) +{ + int tmp; + + smp_wmb(); + tmp = atomic_sub_return(1,(atomic_t *)(&sem->count)); + if (tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0) + rwsem_wake(sem); +} + +/* + * unlock after writing + */ +static inline void __up_write(struct rw_semaphore *sem) +{ + smp_wmb(); + if (atomic_sub_return(RWSEM_ACTIVE_WRITE_BIAS, + (atomic_t *)(&sem->count)) < 0) + rwsem_wake(sem); +} + +/* + * implement atomic add functionality + */ +static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) +{ + atomic_add(delta, (atomic_t *)(&sem->count)); +} + +/* + * downgrade write lock to read lock + */ +static inline void __downgrade_write(struct rw_semaphore *sem) +{ + int tmp; + + smp_wmb(); + tmp = atomic_add_return(-RWSEM_WAITING_BIAS, (atomic_t *)(&sem->count)); + if (tmp < 0) + rwsem_downgrade_wake(sem); +} + +/* + * implement exchange and add functionality + */ +static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) +{ + smp_mb(); + return atomic_add_return(delta, (atomic_t *)(&sem->count)); +} + +#endif /* _XTENSA_RWSEM_XADD_H */ diff --git a/include/asm-xtensa/scatterlist.h b/include/asm-xtensa/scatterlist.h new file mode 100644 index 000000000000..38a2b9acd658 --- /dev/null +++ b/include/asm-xtensa/scatterlist.h @@ -0,0 +1,34 @@ +/* + * include/asm-xtensa/scatterlist.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SCATTERLIST_H +#define _XTENSA_SCATTERLIST_H + +struct scatterlist { + struct page *page; + unsigned int offset; + dma_addr_t dma_address; + unsigned int length; +}; + +/* + * These macros should be used after a pci_map_sg call has been done + * to get bus addresses of each of the SG entries and their lengths. + * You should only work with the number of sg entries pci_map_sg + * returns, or alternatively stop on the first sg_dma_len(sg) which + * is 0. + */ +#define sg_dma_address(sg) ((sg)->dma_address) +#define sg_dma_len(sg) ((sg)->length) + + +#define ISA_DMA_THRESHOLD (~0UL) + +#endif /* _XTENSA_SCATTERLIST_H */ diff --git a/include/asm-xtensa/sections.h b/include/asm-xtensa/sections.h new file mode 100644 index 000000000000..40b5191b55a2 --- /dev/null +++ b/include/asm-xtensa/sections.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/sections.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SECTIONS_H +#define _XTENSA_SECTIONS_H + +#include + +#endif /* _XTENSA_SECTIONS_H */ diff --git a/include/asm-xtensa/segment.h b/include/asm-xtensa/segment.h new file mode 100644 index 000000000000..a2eb547a1a75 --- /dev/null +++ b/include/asm-xtensa/segment.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/segment.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SEGMENT_H +#define _XTENSA_SEGMENT_H + +#include + +#endif /* _XTENSA_SEGEMENT_H */ diff --git a/include/asm-xtensa/semaphore.h b/include/asm-xtensa/semaphore.h new file mode 100644 index 000000000000..c8a7574a9a57 --- /dev/null +++ b/include/asm-xtensa/semaphore.h @@ -0,0 +1,129 @@ +/* + * linux/include/asm-xtensa/semaphore.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SEMAPHORE_H +#define _XTENSA_SEMAPHORE_H + +#include +#include +#include +#include + +struct semaphore { + atomic_t count; + int sleepers; + wait_queue_head_t wait; +#if WAITQUEUE_DEBUG + long __magic; +#endif +}; + +#if WAITQUEUE_DEBUG +# define __SEM_DEBUG_INIT(name) \ + , (int)&(name).__magic +#else +# define __SEM_DEBUG_INIT(name) +#endif + +#define __SEMAPHORE_INITIALIZER(name,count) \ + { ATOMIC_INIT(count), \ + 0, \ + __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ + __SEM_DEBUG_INIT(name) } + +#define __MUTEX_INITIALIZER(name) \ + __SEMAPHORE_INITIALIZER(name, 1) + +#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) + +#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) +#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) + +extern inline void sema_init (struct semaphore *sem, int val) +{ +/* + * *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val); + * + * i'd rather use the more flexible initialization above, but sadly + * GCC 2.7.2.3 emits a bogus warning. EGCS doesnt. Oh well. + */ + atomic_set(&sem->count, val); + init_waitqueue_head(&sem->wait); +#if WAITQUEUE_DEBUG + sem->__magic = (int)&sem->__magic; +#endif +} + +static inline void init_MUTEX (struct semaphore *sem) +{ + sema_init(sem, 1); +} + +static inline void init_MUTEX_LOCKED (struct semaphore *sem) +{ + sema_init(sem, 0); +} + +asmlinkage void __down(struct semaphore * sem); +asmlinkage int __down_interruptible(struct semaphore * sem); +asmlinkage int __down_trylock(struct semaphore * sem); +asmlinkage void __up(struct semaphore * sem); + +extern spinlock_t semaphore_wake_lock; + +extern __inline__ void down(struct semaphore * sem) +{ +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + if (atomic_sub_return(1, &sem->count) < 0) + __down(sem); +} + +extern __inline__ int down_interruptible(struct semaphore * sem) +{ + int ret = 0; +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + if (atomic_sub_return(1, &sem->count) < 0) + ret = __down_interruptible(sem); + return ret; +} + +extern __inline__ int down_trylock(struct semaphore * sem) +{ + int ret = 0; +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + if (atomic_sub_return(1, &sem->count) < 0) + ret = __down_trylock(sem); + return ret; +} + +/* + * Note! This is subtle. We jump to wake people up only if + * the semaphore was negative (== somebody was waiting on it). + */ +extern __inline__ void up(struct semaphore * sem) +{ +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + if (atomic_add_return(1, &sem->count) <= 0) + __up(sem); +} + +#endif /* _XTENSA_SEMAPHORE_H */ diff --git a/include/asm-xtensa/sembuf.h b/include/asm-xtensa/sembuf.h new file mode 100644 index 000000000000..2d26c47666fe --- /dev/null +++ b/include/asm-xtensa/sembuf.h @@ -0,0 +1,44 @@ +/* + * include/asm-xtensa/sembuf.h + * + * The semid64_ds structure for Xtensa 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) 2001 - 2005 Tensilica Inc. + * + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + * + */ + +#ifndef _XTENSA_SEMBUF_H +#define _XTENSA_SEMBUF_H + +#include + +struct semid64_ds { + struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ +#if XCHAL_HAVE_LE + __kernel_time_t sem_otime; /* last semop time */ + unsigned long __unused1; + __kernel_time_t sem_ctime; /* last change time */ + unsigned long __unused2; +#else + unsigned long __unused1; + __kernel_time_t sem_otime; /* last semop time */ + unsigned long __unused2; + __kernel_time_t sem_ctime; /* last change time */ +#endif + unsigned long sem_nsems; /* no. of semaphores in array */ + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* __ASM_XTENSA_SEMBUF_H */ diff --git a/include/asm-xtensa/serial.h b/include/asm-xtensa/serial.h new file mode 100644 index 000000000000..ec04114fcf0b --- /dev/null +++ b/include/asm-xtensa/serial.h @@ -0,0 +1,18 @@ +/* + * include/asm-xtensa/serial.h + * + * Configuration details for 8250, 16450, 16550, etc. serial ports + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SERIAL_H +#define _XTENSA_SERIAL_H + +#include + +#endif /* _XTENSA_SERIAL_H */ diff --git a/include/asm-xtensa/setup.h b/include/asm-xtensa/setup.h new file mode 100644 index 000000000000..e3636520d8cc --- /dev/null +++ b/include/asm-xtensa/setup.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/setup.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SETUP_H +#define _XTENSA_SETUP_H + +#define COMMAND_LINE_SIZE 256 + +#endif diff --git a/include/asm-xtensa/shmbuf.h b/include/asm-xtensa/shmbuf.h new file mode 100644 index 000000000000..a30b81a4b933 --- /dev/null +++ b/include/asm-xtensa/shmbuf.h @@ -0,0 +1,50 @@ +/* + * include/asm-xtensa/shmbuf.h + * + * The shmid64_ds structure for Xtensa architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SHMBUF_H +#define _XTENSA_SHMBUF_H + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ + size_t shm_segsz; /* size of segment (bytes) */ + __kernel_time_t shm_atime; /* last attach time */ + unsigned long __unused1; + __kernel_time_t shm_dtime; /* last detach time */ + unsigned long __unused2; + __kernel_time_t shm_ctime; /* last change time */ + unsigned long __unused3; + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + unsigned long shm_nattch; /* no. of current attaches */ + unsigned long __unused4; + unsigned long __unused5; +}; + +struct shminfo64 { + unsigned long shmmax; + unsigned long shmmin; + unsigned long shmmni; + unsigned long shmseg; + unsigned long shmall; + unsigned long __unused1; + unsigned long __unused2; + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _XTENSA_SHMBUF_H */ diff --git a/include/asm-xtensa/shmparam.h b/include/asm-xtensa/shmparam.h new file mode 100644 index 000000000000..d3b65bfa71c3 --- /dev/null +++ b/include/asm-xtensa/shmparam.h @@ -0,0 +1,23 @@ +/* + * include/asm-xtensa/shmparam.h + * + * 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 _XTENSA_SHMPARAM_H +#define _XTENSA_SHMPARAM_H + +#include + +/* + * Xtensa can have variable size caches, and if + * the size of single way is larger than the page size, + * then we have to start worrying about cache aliasing + * problems. + */ + +#define SHMLBA ((PAGE_SIZE > DCACHE_WAY_SIZE)? PAGE_SIZE : DCACHE_WAY_SIZE) + +#endif /* _XTENSA_SHMPARAM_H */ diff --git a/include/asm-xtensa/sigcontext.h b/include/asm-xtensa/sigcontext.h new file mode 100644 index 000000000000..a75177291418 --- /dev/null +++ b/include/asm-xtensa/sigcontext.h @@ -0,0 +1,44 @@ +/* + * include/asm-xtensa/sigcontext.h + * + * 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) 2001 - 2003 Tensilica Inc. + */ + +#ifndef _XTENSA_SIGCONTEXT_H +#define _XTENSA_SIGCONTEXT_H + +#define _ASMLANGUAGE +#include +#include + + +struct _cpstate { + unsigned char _cpstate[XTENSA_CP_EXTRA_SIZE]; +} __attribute__ ((aligned (XTENSA_CP_EXTRA_ALIGN))); + + +struct sigcontext { + unsigned long oldmask; + + /* CPU registers */ + unsigned long sc_pc; + unsigned long sc_ps; + unsigned long sc_wmask; + unsigned long sc_windowbase; + unsigned long sc_windowstart; + unsigned long sc_lbeg; + unsigned long sc_lend; + unsigned long sc_lcount; + unsigned long sc_sar; + unsigned long sc_depc; + unsigned long sc_dareg0; + unsigned long sc_treg[4]; + unsigned long sc_areg[XCHAL_NUM_AREGS]; + struct _cpstate *sc_cpstate; +}; + +#endif /* __ASM_XTENSA_SIGCONTEXT_H */ diff --git a/include/asm-xtensa/siginfo.h b/include/asm-xtensa/siginfo.h new file mode 100644 index 000000000000..44f0ae77b539 --- /dev/null +++ b/include/asm-xtensa/siginfo.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/processor.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SIGINFO_H +#define _XTENSA_SIGINFO_H + +#include + +#endif /* _XTENSA_SIGINFO_H */ diff --git a/include/asm-xtensa/signal.h b/include/asm-xtensa/signal.h new file mode 100644 index 000000000000..5d6fc9cdf58d --- /dev/null +++ b/include/asm-xtensa/signal.h @@ -0,0 +1,187 @@ +/* + * include/asm-xtensa/signal.h + * + * Swiped from 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SIGNAL_H +#define _XTENSA_SIGNAL_H + + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +#ifndef __ASSEMBLY__ + +#include + +/* Avoid too many header ordering problems. */ +struct siginfo; +typedef unsigned long old_sigset_t; /* at least 32 bits */ +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#endif + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* #define SIGLOST 29 */ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX (_NSIG-1) + +/* + * SA_FLAGS values: + * + * SA_ONSTACK indicates that a registered stack_t will be used. + * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001 +#define SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define SA_SIGINFO 0x00000004 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND +#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ + +#define SA_RESTORER 0x04000000 + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#ifndef __ASSEMBLY__ +#ifdef __KERNEL__ + +/* + * These values of sa_flags are used only by the kernel as part of the + * irq handling routines. + * + * SA_INTERRUPT is also used by the irq handling routines. + * SA_SHIRQ is for shared interrupt support on PCI and EISA. + */ +#define SA_PROBE SA_ONESHOT +#define SA_SAMPLE_RANDOM SA_RESTART +#define SA_SHIRQ 0x04000000 +#endif + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +/* Type of a signal handler. */ +typedef void (*__sighandler_t)(int); + +#define SIG_DFL ((__sighandler_t)0) /* default signal handling */ +#define SIG_IGN ((__sighandler_t)1) /* ignore signal */ +#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */ + +#ifdef __KERNEL__ +struct old_sigaction { + __sighandler_t sa_handler; + old_sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; /* mask last for extensibility */ +}; + +struct k_sigaction { + struct sigaction sa; +}; + +#else + +/* Here we must cater to libcs that poke about in kernel headers. */ + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif /* __KERNEL__ */ + +typedef struct sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + +#ifdef __KERNEL__ +#include +#define ptrace_signal_deliver(regs, cookie) do { } while (0) + +#endif /* __KERNEL__ */ +#endif /* __ASSEMBLY__ */ +#endif /* _XTENSA_SIGNAL_H */ diff --git a/include/asm-xtensa/smp.h b/include/asm-xtensa/smp.h new file mode 100644 index 000000000000..83c569e3bdbd --- /dev/null +++ b/include/asm-xtensa/smp.h @@ -0,0 +1,27 @@ +/* + * include/asm-xtensa/smp.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SMP_H +#define _XTENSA_SMP_H + +extern struct xtensa_cpuinfo boot_cpu_data; + +#define cpu_data (&boot_cpu_data) +#define current_cpu_data boot_cpu_data + +struct xtensa_cpuinfo { + unsigned long *pgd_cache; + unsigned long *pte_cache; + unsigned long pgtable_cache_sz; +}; + +#define cpu_logical_map(cpu) (cpu) + +#endif /* _XTENSA_SMP_H */ diff --git a/include/asm-xtensa/socket.h b/include/asm-xtensa/socket.h new file mode 100644 index 000000000000..daccd05a14cd --- /dev/null +++ b/include/asm-xtensa/socket.h @@ -0,0 +1,61 @@ +/* + * include/asm-xtensa/socket.h + * + * Copied from i386. + * + * 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 _XTENSA_SOCKET_H +#define _XTENSA_SOCKET_H + +#include + +/* For setsockoptions(2) */ +#define SOL_SOCKET 1 + +#define SO_DEBUG 1 +#define SO_REUSEADDR 2 +#define SO_TYPE 3 +#define SO_ERROR 4 +#define SO_DONTROUTE 5 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_KEEPALIVE 9 +#define SO_OOBINLINE 10 +#define SO_NO_CHECK 11 +#define SO_PRIORITY 12 +#define SO_LINGER 13 +#define SO_BSDCOMPAT 14 +/* To add :#define SO_REUSEPORT 15 */ +#define SO_PASSCRED 16 +#define SO_PEERCRED 17 +#define SO_RCVLOWAT 18 +#define SO_SNDLOWAT 19 +#define SO_RCVTIMEO 20 +#define SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ + +#define SO_SECURITY_AUTHENTICATION 22 +#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define SO_BINDTODEVICE 25 + +/* Socket filtering */ + +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + +#define SO_PEERNAME 28 +#define SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define SO_ACCEPTCONN 30 +#define SO_PEERSEC 31 + +#endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-xtensa/sockios.h b/include/asm-xtensa/sockios.h new file mode 100644 index 000000000000..20d2ba10ecd1 --- /dev/null +++ b/include/asm-xtensa/sockios.h @@ -0,0 +1,30 @@ +/* + * include/asm-xtensa/sockios.h + * + * Socket-level I/O control calls. Copied from MIPS. + * + * 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 Ralf Baechle + * Copyright (C) 2001 Tensilica Inc. + */ + +#ifndef _XTENSA_SOCKIOS_H +#define _XTENSA_SOCKIOS_H + +#include + +/* Socket-level I/O control calls. */ + +#define FIOGETOWN _IOR('f', 123, int) +#define FIOSETOWN _IOW('f', 124, int) + +#define SIOCATMARK _IOR('s', 7, int) +#define SIOCSPGRP _IOW('s', 8, pid_t) +#define SIOCGPGRP _IOR('s', 9, pid_t) + +#define SIOCGSTAMP 0x8906 /* Get stamp - linux-specific */ + +#endif /* _XTENSA_SOCKIOS_H */ diff --git a/include/asm-xtensa/spinlock.h b/include/asm-xtensa/spinlock.h new file mode 100644 index 000000000000..8ff23649581b --- /dev/null +++ b/include/asm-xtensa/spinlock.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/spinlock.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SPINLOCK_H +#define _XTENSA_SPINLOCK_H + +#include + +#endif /* _XTENSA_SPINLOCK_H */ diff --git a/include/asm-xtensa/stat.h b/include/asm-xtensa/stat.h new file mode 100644 index 000000000000..2f4662ff6c3a --- /dev/null +++ b/include/asm-xtensa/stat.h @@ -0,0 +1,105 @@ +/* + * include/asm-xtensa/stat.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_STAT_H +#define _XTENSA_STAT_H + +#include + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long st_mtime; + unsigned long st_ctime; +}; + +#define STAT_HAVE_NSEC 1 + +struct stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; + unsigned long __unused4; + unsigned long __unused5; +}; + +/* This matches struct stat64 in glibc-2.2.3. */ + +struct stat64 { +#ifdef __XTENSA_EL__ + unsigned short st_dev; /* Device */ + unsigned char __pad0[10]; +#else + unsigned char __pad0[6]; + unsigned short st_dev; + unsigned char __pad1[2]; +#endif + +#define STAT64_HAS_BROKEN_ST_INO 1 + unsigned long __st_ino; /* 32bit file serial number. */ + + unsigned int st_mode; /* File mode. */ + unsigned int st_nlink; /* Link count. */ + unsigned int st_uid; /* User ID of the file's owner. */ + unsigned int st_gid; /* Group ID of the file's group. */ + +#ifdef __XTENSA_EL__ + unsigned short st_rdev; /* Device number, if device. */ + unsigned char __pad3[10]; +#else + unsigned char __pad2[6]; + unsigned short st_rdev; + unsigned char __pad3[2]; +#endif + + long long int st_size; /* Size of file, in bytes. */ + long int st_blksize; /* Optimal block size for I/O. */ + +#ifdef __XTENSA_EL__ + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long __pad4; +#else + unsigned long __pad4; + unsigned long st_blocks; +#endif + + unsigned long __pad5; + long int st_atime; /* Time of last access. */ + unsigned long st_atime_nsec; + long int st_mtime; /* Time of last modification. */ + unsigned long st_mtime_nsec; + long int st_ctime; /* Time of last status change. */ + unsigned long st_ctime_nsec; + unsigned long long int st_ino; /* File serial number. */ +}; + +#endif /* _XTENSA_STAT_H */ diff --git a/include/asm-xtensa/statfs.h b/include/asm-xtensa/statfs.h new file mode 100644 index 000000000000..9c3d1a213136 --- /dev/null +++ b/include/asm-xtensa/statfs.h @@ -0,0 +1,17 @@ +/* + * include/asm-xtensa/statfs.h + * + * 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) 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_STATFS_H +#define _XTENSA_STATFS_H + +#include + +#endif /* _XTENSA_STATFS_H */ + diff --git a/include/asm-xtensa/string.h b/include/asm-xtensa/string.h new file mode 100644 index 000000000000..3f81b27d9809 --- /dev/null +++ b/include/asm-xtensa/string.h @@ -0,0 +1,124 @@ +/* + * include/asm-xtensa/string.h + * + * These trivial string functions are considered part of the public domain. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +/* We should optimize these. See arch/xtensa/lib/strncpy_user.S */ + +#ifndef _XTENSA_STRING_H +#define _XTENSA_STRING_H + +#define __HAVE_ARCH_STRCPY +extern __inline__ char *strcpy(char *__dest, const char *__src) +{ + register char *__xdest = __dest; + unsigned long __dummy; + + __asm__ __volatile__("1:\n\t" + "l8ui %2, %1, 0\n\t" + "s8i %2, %0, 0\n\t" + "addi %1, %1, 1\n\t" + "addi %0, %0, 1\n\t" + "bnez %2, 1b\n\t" + : "=r" (__dest), "=r" (__src), "=&r" (__dummy) + : "0" (__dest), "1" (__src) + : "memory"); + + return __xdest; +} + +#define __HAVE_ARCH_STRNCPY +extern __inline__ char *strncpy(char *__dest, const char *__src, size_t __n) +{ + register char *__xdest = __dest; + unsigned long __dummy; + + if (__n == 0) + return __xdest; + + __asm__ __volatile__( + "1:\n\t" + "l8ui %2, %1, 0\n\t" + "s8i %2, %0, 0\n\t" + "addi %1, %1, 1\n\t" + "addi %0, %0, 1\n\t" + "beqz %2, 2f\n\t" + "bne %1, %5, 1b\n" + "2:" + : "=r" (__dest), "=r" (__src), "=&r" (__dummy) + : "0" (__dest), "1" (__src), "r" (__src+__n) + : "memory"); + + return __xdest; +} + +#define __HAVE_ARCH_STRCMP +extern __inline__ int strcmp(const char *__cs, const char *__ct) +{ + register int __res; + unsigned long __dummy; + + __asm__ __volatile__( + "1:\n\t" + "l8ui %3, %1, 0\n\t" + "addi %1, %1, 1\n\t" + "l8ui %2, %0, 0\n\t" + "addi %0, %0, 1\n\t" + "beqz %2, 2f\n\t" + "beq %2, %3, 1b\n" + "2:\n\t" + "sub %2, %3, %2" + : "=r" (__cs), "=r" (__ct), "=&r" (__res), "=&r" (__dummy) + : "0" (__cs), "1" (__ct)); + + return __res; +} + +#define __HAVE_ARCH_STRNCMP +extern __inline__ int strncmp(const char *__cs, const char *__ct, size_t __n) +{ + register int __res; + unsigned long __dummy; + + __asm__ __volatile__( + "mov %2, %3\n" + "1:\n\t" + "beq %0, %6, 2f\n\t" + "l8ui %3, %1, 0\n\t" + "addi %1, %1, 1\n\t" + "l8ui %2, %0, 0\n\t" + "addi %0, %0, 1\n\t" + "beqz %2, 2f\n\t" + "beqz %3, 2f\n\t" + "beq %2, %3, 1b\n" + "2:\n\t" + "sub %2, %3, %2" + : "=r" (__cs), "=r" (__ct), "=&r" (__res), "=&r" (__dummy) + : "0" (__cs), "1" (__ct), "r" (__cs+__n)); + + return __res; +} + +#define __HAVE_ARCH_MEMSET +extern void *memset(void *__s, int __c, size_t __count); + +#define __HAVE_ARCH_MEMCPY +extern void *memcpy(void *__to, __const__ void *__from, size_t __n); + +#define __HAVE_ARCH_MEMMOVE +extern void *memmove(void *__dest, __const__ void *__src, size_t __n); + +/* Don't build bcopy at all ... */ +#define __HAVE_ARCH_BCOPY + +#define __HAVE_ARCH_MEMSCAN +#define memscan memchr + +#endif /* _XTENSA_STRING_H */ diff --git a/include/asm-xtensa/system.h b/include/asm-xtensa/system.h new file mode 100644 index 000000000000..690fe325e671 --- /dev/null +++ b/include/asm-xtensa/system.h @@ -0,0 +1,252 @@ +/* + * include/asm-xtensa/system.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_SYSTEM_H +#define _XTENSA_SYSTEM_H + +#include +#include + +#include + +/* interrupt control */ + +#define local_save_flags(x) \ + __asm__ __volatile__ ("rsr %0,"__stringify(PS) : "=a" (x)); +#define local_irq_restore(x) do { \ + __asm__ __volatile__ ("wsr %0, "__stringify(PS)" ; rsync" \ + :: "a" (x) : "memory"); } while(0); +#define local_irq_save(x) do { \ + __asm__ __volatile__ ("rsil %0, "__stringify(LOCKLEVEL) \ + : "=a" (x) :: "memory");} while(0); + +static inline void local_irq_disable(void) +{ + unsigned long flags; + __asm__ __volatile__ ("rsil %0, "__stringify(LOCKLEVEL) + : "=a" (flags) :: "memory"); +} +static inline void local_irq_enable(void) +{ + unsigned long flags; + __asm__ __volatile__ ("rsil %0, 0" : "=a" (flags) :: "memory"); + +} + +static inline int irqs_disabled(void) +{ + unsigned long flags; + local_save_flags(flags); + return flags & 0xf; +} + +#define RSR_CPENABLE(x) do { \ + __asm__ __volatile__("rsr %0," __stringify(CPENABLE) : "=a" (x)); \ + } while(0); +#define WSR_CPENABLE(x) do { \ + __asm__ __volatile__("wsr %0," __stringify(CPENABLE)";rsync" \ + :: "a" (x));} while(0); + +#define clear_cpenable() __clear_cpenable() + +extern __inline__ void __clear_cpenable(void) +{ +#if XCHAL_HAVE_CP + unsigned long i = 0; + WSR_CPENABLE(i); +#endif +} + +extern __inline__ void enable_coprocessor(int i) +{ +#if XCHAL_HAVE_CP + int cp; + RSR_CPENABLE(cp); + cp |= 1 << i; + WSR_CPENABLE(cp); +#endif +} + +extern __inline__ void disable_coprocessor(int i) +{ +#if XCHAL_HAVE_CP + int cp; + RSR_CPENABLE(cp); + cp &= ~(1 << i); + WSR_CPENABLE(cp); +#endif +} + +#define smp_read_barrier_depends() do { } while(0) +#define read_barrier_depends() do { } while(0) + +#define mb() barrier() +#define rmb() mb() +#define wmb() mb() + +#ifdef CONFIG_SMP +#error smp_* not defined +#else +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#endif + +#define set_mb(var, value) do { var = value; mb(); } while (0) +#define set_wmb(var, value) do { var = value; wmb(); } while (0) + +#if !defined (__ASSEMBLY__) + +/* * switch_to(n) should switch tasks to task nr n, first + * checking that n isn't the current task, in which case it does nothing. + */ +extern void *_switch_to(void *last, void *next); + +#endif /* __ASSEMBLY__ */ + +#define prepare_to_switch() do { } while(0) + +#define switch_to(prev,next,last) \ +do { \ + clear_cpenable(); \ + (last) = _switch_to(prev, next); \ +} while(0) + +/* + * cmpxchg + */ + +extern __inline__ unsigned long +__cmpxchg_u32(volatile int *p, int old, int new) +{ + __asm__ __volatile__("rsil a15, "__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %1, 0 \n\t" + "bne %0, %2, 1f \n\t" + "s32i %3, %1, 0 \n\t" + "1: \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n\t" + : "=&a" (old) + : "a" (p), "a" (old), "r" (new) + : "a15", "memory"); + return old; +} +/* This function doesn't exist, so you'll get a linker error + * if something tries to do an invalid cmpxchg(). */ + +extern void __cmpxchg_called_with_bad_pointer(void); + +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); + default: __cmpxchg_called_with_bad_pointer(); + 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))); \ + }) + + + + +/* + * xchg_u32 + * + * Note that a15 is used here because the register allocation + * done by the compiler is not guaranteed and a window overflow + * may not occur between the rsil and wsr instructions. By using + * a15 in the rsil, the machine is guaranteed to be in a state + * where no register reference will cause an overflow. + */ + +extern __inline__ unsigned long xchg_u32(volatile int * m, unsigned long val) +{ + unsigned long tmp; + __asm__ __volatile__("rsil a15, "__stringify(LOCKLEVEL)"\n\t" + "l32i %0, %1, 0 \n\t" + "s32i %2, %1, 0 \n\t" + "wsr a15, "__stringify(PS)" \n\t" + "rsync \n\t" + : "=&a" (tmp) + : "a" (m), "a" (val) + : "a15", "memory"); + return tmp; +} + +#define tas(ptr) (xchg((ptr),1)) + +#if ( __XCC__ == 1 ) + +/* xt-xcc processes __inline__ differently than xt-gcc and decides to + * insert an out-of-line copy of function __xchg. This presents the + * unresolved symbol at link time of __xchg_called_with_bad_pointer, + * even though such a function would never be called at run-time. + * xt-gcc always inlines __xchg, and optimizes away the undefined + * bad_pointer function. + */ + +#define xchg(ptr,x) xchg_u32(ptr,x) + +#else /* assume xt-gcc */ + +#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) + +/* + * This only works if the compiler isn't horribly bad at optimizing. + * gcc-2.5.8 reportedly can't handle this, but I define that one to + * be dead anyway. + */ + +extern void __xchg_called_with_bad_pointer(void); + +static __inline__ unsigned long +__xchg(unsigned long x, volatile void * ptr, int size) +{ + switch (size) { + case 4: + return xchg_u32(ptr, x); + } + __xchg_called_with_bad_pointer(); + return x; +} + +#endif + +extern void set_except_vector(int n, void *addr); + +static inline void spill_registers(void) +{ + unsigned int a0, ps; + + __asm__ __volatile__ ( + "movi a14," __stringify (PS_EXCM_MASK) " | 1\n\t" + "mov a12, a0\n\t" + "rsr a13," __stringify(SAR) "\n\t" + "xsr a14," __stringify(PS) "\n\t" + "movi a0, _spill_registers\n\t" + "rsync\n\t" + "callx0 a0\n\t" + "mov a0, a12\n\t" + "wsr a13," __stringify(SAR) "\n\t" + "wsr a14," __stringify(PS) "\n\t" + :: "a" (&a0), "a" (&ps) + : "a2", "a3", "a12", "a13", "a14", "a15", "memory"); +} + +#define arch_align_stack(x) (x) + +#endif /* _XTENSA_SYSTEM_H */ diff --git a/include/asm-xtensa/termbits.h b/include/asm-xtensa/termbits.h new file mode 100644 index 000000000000..c780593ff5f9 --- /dev/null +++ b/include/asm-xtensa/termbits.h @@ -0,0 +1,194 @@ +/* + * include/asm-xtensa/termbits.h + * + * Copied from 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_TERMBITS_H +#define _XTENSA_TERMBITS_H + + +#include + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +/* c_cc characters */ + +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ + +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +/* c_oflag bits */ + +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ + +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CBAUDEX 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate (not used) */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ + +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* tcflow() and TCXONC use these */ + +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ + +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ + +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif /* _XTENSA_TERMBITS_H */ diff --git a/include/asm-xtensa/termios.h b/include/asm-xtensa/termios.h new file mode 100644 index 000000000000..83c6aed1d115 --- /dev/null +++ b/include/asm-xtensa/termios.h @@ -0,0 +1,122 @@ +/* + * include/asm-xtensa/termios.h + * + * Copied from 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_TERMIOS_H +#define _XTENSA_TERMIOS_H + +#include +#include + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +/* Modem lines */ + +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG +#define TIOCM_OUT1 0x2000 +#define TIOCM_OUT2 0x4000 +#define TIOCM_LOOP 0x8000 + +/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ + +/* Line disciplines */ + +#define N_TTY 0 +#define N_SLIP 1 +#define N_MOUSE 2 +#define N_PPP 3 +#define N_STRIP 4 +#define N_AX25 5 +#define N_X25 6 /* X.25 async */ +#define N_6PACK 7 +#define N_MASC 8 /* Reserved for Mobitex module */ +#define N_R3964 9 /* Reserved for Simatic R3964 module */ +#define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ +#define N_IRDA 11 /* Linux IR - http://irda.sourceforge.net/ */ +#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ +#define N_SYNC_PPP 14 +#define N_HCI 15 /* Bluetooth HCI UART */ + +#ifdef __KERNEL__ + +/* intr=^C quit=^\ erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" + +/* + * Translate a "termio" structure into a "termios". Ugh. + */ + +#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ + unsigned short __tmp; \ + get_user(__tmp,&(termio)->x); \ + *(unsigned short *) &(termios)->x = __tmp; \ +} + +#define user_termio_to_kernel_termios(termios, termio) \ +({ \ + SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ + copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ +}) + +/* + * Translate a "termios" structure into a "termio". Ugh. + */ + +#define kernel_termios_to_user_termio(termio, termios) \ +({ \ + put_user((termios)->c_iflag, &(termio)->c_iflag); \ + put_user((termios)->c_oflag, &(termio)->c_oflag); \ + put_user((termios)->c_cflag, &(termio)->c_cflag); \ + put_user((termios)->c_lflag, &(termio)->c_lflag); \ + put_user((termios)->c_line, &(termio)->c_line); \ + copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ +}) + +#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios)) +#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios)) + +#endif /* __KERNEL__ */ + +#endif /* _XTENSA_TERMIOS_H */ diff --git a/include/asm-xtensa/thread_info.h b/include/asm-xtensa/thread_info.h new file mode 100644 index 000000000000..af208d41fd82 --- /dev/null +++ b/include/asm-xtensa/thread_info.h @@ -0,0 +1,146 @@ +/* + * include/asm-xtensa/thread_info.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_THREAD_INFO_H +#define _XTENSA_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +# include +#endif + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + * - if the contents of this structure are changed, the assembly constants + * must also be changed + */ + +#ifndef __ASSEMBLY__ + +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + unsigned long flags; /* low level flags */ + unsigned long status; /* thread-synchronous flags */ + __u32 cpu; /* current CPU */ + __s32 preempt_count; /* 0 => preemptable,< 0 => BUG*/ + + mm_segment_t addr_limit; /* thread address space */ + struct restart_block restart_block; + + +}; + +#else /* !__ASSEMBLY__ */ + +/* offsets into the thread_info struct for assembly code access */ +#define TI_TASK 0x00000000 +#define TI_EXEC_DOMAIN 0x00000004 +#define TI_FLAGS 0x00000008 +#define TI_STATUS 0x0000000C +#define TI_CPU 0x00000010 +#define TI_PRE_COUNT 0x00000014 +#define TI_ADDR_LIMIT 0x00000018 +#define TI_RESTART_BLOCK 0x000001C + +#endif + +#define PREEMPT_ACTIVE 0x10000000 + +/* + * macros/functions for gaining access to the thread information structure + * + * preempt_count needs to be 1 initially, until the scheduler is functional. + */ + +#ifndef __ASSEMBLY__ + +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = 1, \ + .addr_limit = KERNEL_DS, \ + .restart_block = { \ + .fn = do_no_restart_syscall, \ + }, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* how to get the thread information struct from C */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + __asm__("extui %0,a1,0,13\n\t" + "xor %0, a1, %0" : "=&r" (ti) : ); + return ti; +} + +/* thread information allocation */ +#define alloc_thread_info(tsk) ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#else /* !__ASSEMBLY__ */ + +/* how to get the thread information struct from ASM */ +#define GET_THREAD_INFO(reg,sp) \ + extui reg, sp, 0, 13; \ + xor reg, sp, reg +#endif + + +/* + * thread information flags + * - these are process state flags that various assembly files may need to access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */ +#define TIF_IRET 5 /* return with iret */ +#define TIF_MEMDIE 6 +#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ + +#define _TIF_SYSCALL_TRACE (1< +#include + +#if XCHAL_INT_LEVEL(XCHAL_TIMER0_INTERRUPT) == 1 +# define LINUX_TIMER 0 +#elif XCHAL_INT_LEVEL(XCHAL_TIMER1_INTERRUPT) == 1 +# define LINUX_TIMER 1 +#elif XCHAL_INT_LEVEL(XCHAL_TIMER2_INTERRUPT) == 1 +# define LINUX_TIMER 2 +#else +# error "Bad timer number for Linux configurations!" +#endif + +#define LINUX_TIMER_INT XCHAL_TIMER_INTERRUPT(LINUX_TIMER) +#define LINUX_TIMER_MASK (1L << LINUX_TIMER_INT) + +#define CLOCK_TICK_RATE 1193180 /* (everyone is using this value) */ +#define CLOCK_TICK_FACTOR 20 /* Factor of both 10^6 and CLOCK_TICK_RATE */ +#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \ + (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \ + << (SHIFT_SCALE-SHIFT_HZ)) / HZ) + +#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT +extern unsigned long ccount_per_jiffy; +extern unsigned long ccount_nsec; +#define CCOUNT_PER_JIFFY ccount_per_jiffy +#define CCOUNT_NSEC ccount_nsec +#else +#define CCOUNT_PER_JIFFY (CONFIG_XTENSA_CPU_CLOCK*(1000000UL/HZ)) +#define CCOUNT_NSEC (1000000000UL / CONFIG_XTENSA_CPU_CLOCK) +#endif + + +typedef unsigned long long cycles_t; + +/* + * Only used for SMP. + */ + +extern cycles_t cacheflush_time; + +#define get_cycles() (0) + + +/* + * Register access. + */ + +#define WSR_CCOUNT(r) __asm__("wsr %0,"__stringify(CCOUNT) :: "a" (r)) +#define RSR_CCOUNT(r) __asm__("rsr %0,"__stringify(CCOUNT) : "=a" (r)) +#define WSR_CCOMPARE(x,r) __asm__("wsr %0,"__stringify(CCOMPARE_0)"+"__stringify(x) :: "a"(r)) +#define RSR_CCOMPARE(x,r) __asm__("rsr %0,"__stringify(CCOMPARE_0)"+"__stringify(x) : "=a"(r)) + +static inline unsigned long get_ccount (void) +{ + unsigned long ccount; + RSR_CCOUNT(ccount); + return ccount; +} + +static inline void set_ccount (unsigned long ccount) +{ + WSR_CCOUNT(ccount); +} + +static inline unsigned long get_linux_timer (void) +{ + unsigned ccompare; + RSR_CCOMPARE(LINUX_TIMER, ccompare); + return ccompare; +} + +static inline void set_linux_timer (unsigned long ccompare) +{ + WSR_CCOMPARE(LINUX_TIMER, ccompare); +} + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_TIMEX_H */ diff --git a/include/asm-xtensa/tlb.h b/include/asm-xtensa/tlb.h new file mode 100644 index 000000000000..4562b2dcfbc0 --- /dev/null +++ b/include/asm-xtensa/tlb.h @@ -0,0 +1,25 @@ +/* + * include/asm-xtensa/tlb.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_TLB_H +#define _XTENSA_TLB_H + +#define tlb_start_vma(tlb,vma) do { } while (0) +#define tlb_end_vma(tlb,vma) do { } while (0) +#define __tlb_remove_tlb_entry(tlb,pte,addr) do { } while (0) + +#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) + +#include +#include + +#define __pte_free_tlb(tlb,pte) pte_free(pte) + +#endif /* _XTENSA_TLB_H */ diff --git a/include/asm-xtensa/tlbflush.h b/include/asm-xtensa/tlbflush.h new file mode 100644 index 000000000000..23bfe9db45f5 --- /dev/null +++ b/include/asm-xtensa/tlbflush.h @@ -0,0 +1,200 @@ +/* + * include/asm-xtensa/tlbflush.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_TLBFLUSH_H +#define _XTENSA_TLBFLUSH_H + +#define DEBUG_TLB + +#ifdef __KERNEL__ + +#include +#include + +/* TLB flushing: + * + * - flush_tlb_all() flushes all processes TLB entries + * - flush_tlb_mm(mm) flushes the specified mm context TLB entries + * - flush_tlb_page(mm, vmaddr) flushes a single page + * - flush_tlb_range(mm, start, end) flushes a range of pages + */ + +extern void flush_tlb_all(void); +extern void flush_tlb_mm(struct mm_struct*); +extern void flush_tlb_page(struct vm_area_struct*,unsigned long); +extern void flush_tlb_range(struct vm_area_struct*,unsigned long,unsigned long); + +#define flush_tlb_kernel_range(start,end) flush_tlb_all() + + +/* This is calld in munmap when we have freed up some page-table pages. + * We don't need to do anything here, there's nothing special about our + * page-table pages. + */ + +extern inline void flush_tlb_pgtables(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + +/* TLB operations. */ + +#define ITLB_WAYS_LOG2 XCHAL_ITLB_WAY_BITS +#define DTLB_WAYS_LOG2 XCHAL_DTLB_WAY_BITS +#define ITLB_PROBE_SUCCESS (1 << ITLB_WAYS_LOG2) +#define DTLB_PROBE_SUCCESS (1 << DTLB_WAYS_LOG2) + +extern inline unsigned long itlb_probe(unsigned long addr) +{ + unsigned long tmp; + __asm__ __volatile__("pitlb %0, %1\n\t" : "=a" (tmp) : "a" (addr)); + return tmp; +} + +extern inline unsigned long dtlb_probe(unsigned long addr) +{ + unsigned long tmp; + __asm__ __volatile__("pdtlb %0, %1\n\t" : "=a" (tmp) : "a" (addr)); + return tmp; +} + +extern inline void invalidate_itlb_entry (unsigned long probe) +{ + __asm__ __volatile__("iitlb %0; isync\n\t" : : "a" (probe)); +} + +extern inline void invalidate_dtlb_entry (unsigned long probe) +{ + __asm__ __volatile__("idtlb %0; dsync\n\t" : : "a" (probe)); +} + +/* Use the .._no_isync functions with caution. Generally, these are + * handy for bulk invalidates followed by a single 'isync'. The + * caller must follow up with an 'isync', which can be relatively + * expensive on some Xtensa implementations. + */ +extern inline void invalidate_itlb_entry_no_isync (unsigned entry) +{ + /* Caller must follow up with 'isync'. */ + __asm__ __volatile__ ("iitlb %0\n" : : "a" (entry) ); +} + +extern inline void invalidate_dtlb_entry_no_isync (unsigned entry) +{ + /* Caller must follow up with 'isync'. */ + __asm__ __volatile__ ("idtlb %0\n" : : "a" (entry) ); +} + +extern inline void set_itlbcfg_register (unsigned long val) +{ + __asm__ __volatile__("wsr %0, "__stringify(ITLBCFG)"\n\t" "isync\n\t" + : : "a" (val)); +} + +extern inline void set_dtlbcfg_register (unsigned long val) +{ + __asm__ __volatile__("wsr %0, "__stringify(DTLBCFG)"; dsync\n\t" + : : "a" (val)); +} + +extern inline void set_ptevaddr_register (unsigned long val) +{ + __asm__ __volatile__(" wsr %0, "__stringify(PTEVADDR)"; isync\n" + : : "a" (val)); +} + +extern inline unsigned long read_ptevaddr_register (void) +{ + unsigned long tmp; + __asm__ __volatile__("rsr %0, "__stringify(PTEVADDR)"\n\t" : "=a" (tmp)); + return tmp; +} + +extern inline void write_dtlb_entry (pte_t entry, int way) +{ + __asm__ __volatile__("wdtlb %1, %0; dsync\n\t" + : : "r" (way), "r" (entry) ); +} + +extern inline void write_itlb_entry (pte_t entry, int way) +{ + __asm__ __volatile__("witlb %1, %0; isync\n\t" + : : "r" (way), "r" (entry) ); +} + +extern inline void invalidate_page_directory (void) +{ + invalidate_dtlb_entry (DTLB_WAY_PGTABLE); +} + +extern inline void invalidate_itlb_mapping (unsigned address) +{ + unsigned long tlb_entry; + while ((tlb_entry = itlb_probe (address)) & ITLB_PROBE_SUCCESS) + invalidate_itlb_entry (tlb_entry); +} + +extern inline void invalidate_dtlb_mapping (unsigned address) +{ + unsigned long tlb_entry; + while ((tlb_entry = dtlb_probe (address)) & DTLB_PROBE_SUCCESS) + invalidate_dtlb_entry (tlb_entry); +} + +#define check_pgt_cache() do { } while (0) + + +#ifdef DEBUG_TLB + +/* DO NOT USE THESE FUNCTIONS. These instructions aren't part of the Xtensa + * ISA and exist only for test purposes.. + * You may find it helpful for MMU debugging, however. + * + * 'at' is the unmodified input register + * 'as' is the output register, as follows (specific to the Linux config): + * + * as[31..12] contain the virtual address + * as[11..08] are meaningless + * as[07..00] contain the asid + */ + +extern inline unsigned long read_dtlb_virtual (int way) +{ + unsigned long tmp; + __asm__ __volatile__("rdtlb0 %0, %1\n\t" : "=a" (tmp), "+a" (way)); + return tmp; +} + +extern inline unsigned long read_dtlb_translation (int way) +{ + unsigned long tmp; + __asm__ __volatile__("rdtlb1 %0, %1\n\t" : "=a" (tmp), "+a" (way)); + return tmp; +} + +extern inline unsigned long read_itlb_virtual (int way) +{ + unsigned long tmp; + __asm__ __volatile__("ritlb0 %0, %1\n\t" : "=a" (tmp), "+a" (way)); + return tmp; +} + +extern inline unsigned long read_itlb_translation (int way) +{ + unsigned long tmp; + __asm__ __volatile__("ritlb1 %0, %1\n\t" : "=a" (tmp), "+a" (way)); + return tmp; +} + +#endif /* DEBUG_TLB */ + + +#endif /* __KERNEL__ */ +#endif /* _XTENSA_PGALLOC_H */ diff --git a/include/asm-xtensa/topology.h b/include/asm-xtensa/topology.h new file mode 100644 index 000000000000..7309e38a0ccb --- /dev/null +++ b/include/asm-xtensa/topology.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/topology.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_TOPOLOGY_H +#define _XTENSA_TOPOLOGY_H + +#include + +#endif /* _XTENSA_TOPOLOGY_H */ diff --git a/include/asm-xtensa/types.h b/include/asm-xtensa/types.h new file mode 100644 index 000000000000..ebac00469852 --- /dev/null +++ b/include/asm-xtensa/types.h @@ -0,0 +1,66 @@ +/* + * include/asm-xtensa/types.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_TYPES_H +#define _XTENSA_TYPES_H + +#ifndef __ASSEMBLY__ + +typedef unsigned short umode_t; + +/* + * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the + * header files exported to user space + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +typedef __signed__ long long __s64; +typedef unsigned long long __u64; +#endif + +/* + * These aren't exported outside the kernel to avoid name space clashes + */ +#ifdef __KERNEL__ + +typedef __signed__ char s8; +typedef unsigned char u8; + +typedef __signed__ short s16; +typedef unsigned short u16; + +typedef __signed__ int s32; +typedef unsigned int u32; + +typedef __signed__ long long s64; +typedef unsigned long long u64; + + +#define BITS_PER_LONG 32 + +/* Dma addresses are 32-bits wide. */ + +typedef u32 dma_addr_t; + +typedef unsigned int kmem_bufctl_t; + +#endif /* __KERNEL__ */ +#endif + +#endif /* _XTENSA_TYPES_H */ diff --git a/include/asm-xtensa/uaccess.h b/include/asm-xtensa/uaccess.h new file mode 100644 index 000000000000..35576b25c7b2 --- /dev/null +++ b/include/asm-xtensa/uaccess.h @@ -0,0 +1,532 @@ +/* + * include/asm-xtensa/uaccess.h + * + * User space memory access functions + * + * These routines provide basic accessing functions to the user memory + * space for the kernel. This header file provides fuctions such as: + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_UACCESS_H +#define _XTENSA_UACCESS_H + +#include + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +#ifdef __ASSEMBLY__ + +#define _ASMLANGUAGE +#include +#include +#include + +/* + * These assembly macros mirror the C macros that follow below. They + * should always have identical functionality. See + * arch/xtensa/kernel/sys.S for usage. + */ + +#define KERNEL_DS 0 +#define USER_DS 1 + +#define get_ds (KERNEL_DS) + +/* + * get_fs reads current->thread.current_ds into a register. + * On Entry: + * anything + * stack + * On Exit: + * contains current->thread.current_ds + */ + .macro get_fs ad, sp + GET_CURRENT(\ad,\sp) + l32i \ad, \ad, THREAD_CURRENT_DS + .endm + +/* + * set_fs sets current->thread.current_ds to some value. + * On Entry: + * anything (temp register) + * value to write + * stack + * On Exit: + * destroyed (actually, current) + * preserved, value to write + */ + .macro set_fs at, av, sp + GET_CURRENT(\at,\sp) + s32i \av, \at, THREAD_CURRENT_DS + .endm + +/* + * kernel_ok determines whether we should bypass addr/size checking. + * See the equivalent C-macro version below for clarity. + * On success, kernel_ok branches to a label indicated by parameter + * . This implies that the macro falls through to the next + * insruction on an error. + * + * Note that while this macro can be used independently, we designed + * in for optimal use in the access_ok macro below (i.e., we fall + * through on error). + * + * On Entry: + * anything (temp register) + * label to branch to on success; implies + * fall-through macro on error + * stack pointer + * On Exit: + * destroyed (actually, current->thread.current_ds) + */ + +#if ((KERNEL_DS != 0) || (USER_DS == 0)) +# error Assembly macro kernel_ok fails +#endif + .macro kernel_ok at, sp, success + get_fs \at, \sp + beqz \at, \success + .endm + +/* + * user_ok determines whether the access to user-space memory is allowed. + * See the equivalent C-macro version below for clarity. + * + * On error, user_ok branches to a label indicated by parameter + * . This implies that the macro falls through to the next + * instruction on success. + * + * Note that while this macro can be used independently, we designed + * in for optimal use in the access_ok macro below (i.e., we fall + * through on success). + * + * On Entry: + * register containing memory address + * register containing memory size + * temp register + * label to branch to on error; implies fall-through + * macro on success + * On Exit: + * preserved + * preserved + * destroyed (actually, (TASK_SIZE + 1 - size)) + */ + .macro user_ok aa, as, at, error + movi \at, (TASK_SIZE+1) + bgeu \as, \at, \error + sub \at, \at, \as + bgeu \aa, \at, \error + .endm + +/* + * access_ok determines whether a memory access is allowed. See the + * equivalent C-macro version below for clarity. + * + * On error, access_ok branches to a label indicated by parameter + * . This implies that the macro falls through to the next + * instruction on success. + * + * Note that we assume success is the common case, and we optimize the + * branch fall-through case on success. + * + * On Entry: + * register containing memory address + * register containing memory size + * temp register + * + * label to branch to on error; implies fall-through + * macro on success + * On Exit: + * preserved + * preserved + * destroyed + */ + .macro access_ok aa, as, at, sp, error + kernel_ok \at, \sp, .Laccess_ok_\@ + user_ok \aa, \as, \at, \error +.Laccess_ok_\@: + .endm + +/* + * verify_area determines whether a memory access is allowed. It's + * mostly an unnecessary wrapper for access_ok, but we provide it as a + * duplicate of the verify_area() C inline function below. See the + * equivalent C version below for clarity. + * + * On error, verify_area branches to a label indicated by parameter + * . This implies that the macro falls through to the next + * instruction on success. + * + * Note that we assume success is the common case, and we optimize the + * branch fall-through case on success. + * + * On Entry: + * register containing memory address + * register containing memory size + * temp register + * label to branch to on error; implies fall-through + * macro on success + * On Exit: + * preserved + * preserved + * destroyed + */ + .macro verify_area aa, as, at, sp, error + access_ok \at, \aa, \as, \sp, \error + .endm + + +#else /* __ASSEMBLY__ not defined */ + +#include +#include + +/* + * The fs value determines whether argument validity checking should + * be performed or not. If get_fs() == USER_DS, checking is + * performed, with get_fs() == KERNEL_DS, checking is bypassed. + * + * For historical reasons (Data Segment Register?), these macros are + * grossly misnamed. + */ + +#define KERNEL_DS ((mm_segment_t) { 0 }) +#define USER_DS ((mm_segment_t) { 1 }) + +#define get_ds() (KERNEL_DS) +#define get_fs() (current->thread.current_ds) +#define set_fs(val) (current->thread.current_ds = (val)) + +#define segment_eq(a,b) ((a).seg == (b).seg) + +#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) +#define __user_ok(addr,size) (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) +#define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size))) +#define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size)) + +extern inline int verify_area(int type, const void * addr, unsigned long size) +{ + return access_ok(type,addr,size) ? 0 : -EFAULT; +} + +/* + * These are the main single-value transfer routines. They + * automatically use the right size if we just have the right pointer + * type. + * + * This gets kind of ugly. We want to return _two_ values in + * "get_user()" and yet we don't want to do any pointers, because that + * is too much of a performance impact. Thus we have a few rather ugly + * macros here, and hide all the uglyness from the user. + * + * Careful to not + * (a) re-use the arguments for side effects (sizeof is ok) + * (b) require any knowledge of processes at this stage + */ +#define put_user(x,ptr) __put_user_check((x),(ptr),sizeof(*(ptr))) +#define get_user(x,ptr) __get_user_check((x),(ptr),sizeof(*(ptr))) + +/* + * The "__xxx" versions of the user access functions are versions that + * do not verify the address space, that must have been done previously + * with a separate "access_ok()" call (this is used when we do multiple + * accesses to the same area of user memory). + */ +#define __put_user(x,ptr) __put_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr))) + + +extern long __put_user_bad(void); + +#define __put_user_nocheck(x,ptr,size) \ +({ \ + long __pu_err; \ + __put_user_size((x),(ptr),(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_check(x,ptr,size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ + __put_user_size((x),__pu_addr,(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __put_user_asm(x,ptr,retval,1,"s8i"); break; \ + case 2: __put_user_asm(x,ptr,retval,2,"s16i"); break; \ + case 4: __put_user_asm(x,ptr,retval,4,"s32i"); break; \ + case 8: { \ + __typeof__(*ptr) __v64 = x; \ + retval = __copy_to_user(ptr,&__v64,8); \ + break; \ + } \ + default: __put_user_bad(); \ + } \ +} while (0) + + +/* + * Consider a case of a user single load/store would cause both an + * unaligned exception and an MMU-related exception (unaligned + * exceptions happen first): + * + * User code passes a bad variable ptr to a system call. + * Kernel tries to access the variable. + * Unaligned exception occurs. + * Unaligned exception handler tries to make aligned accesses. + * Double exception occurs for MMU-related cause (e.g., page not mapped). + * do_page_fault() thinks the fault address belongs to the kernel, not the + * user, and panics. + * + * The kernel currently prohibits user unaligned accesses. We use the + * __check_align_* macros to check for unaligned addresses before + * accessing user space so we don't crash the kernel. Both + * __put_user_asm and __get_user_asm use these alignment macros, so + * macro-specific labels such as 0f, 1f, %0, %2, and %3 must stay in + * sync. + */ + +#define __check_align_1 "" + +#define __check_align_2 \ + " _bbci.l %2, 0, 1f \n" \ + " movi %0, %3 \n" \ + " _j 2f \n" + +#define __check_align_4 \ + " _bbsi.l %2, 0, 0f \n" \ + " _bbci.l %2, 1, 1f \n" \ + "0: movi %0, %3 \n" \ + " _j 2f \n" + + +/* + * We don't tell gcc that we are accessing memory, but this is OK + * because we do not write to any memory gcc knows about, so there + * are no aliasing issues. + * + * WARNING: If you modify this macro at all, verify that the + * __check_align_* macros still work. + */ +#define __put_user_asm(x, addr, err, align, insn) \ + __asm__ __volatile__( \ + __check_align_##align \ + "1: "insn" %1, %2, 0 \n" \ + "2: \n" \ + " .section .fixup,\"ax\" \n" \ + " .align 4 \n" \ + "4: \n" \ + " .long 2b \n" \ + "5: \n" \ + " l32r %2, 4b \n" \ + " movi %0, %3 \n" \ + " jx %2 \n" \ + " .previous \n" \ + " .section __ex_table,\"a\" \n" \ + " .long 1b, 5b \n" \ + " .previous" \ + :"=r" (err) \ + :"r" ((int)(x)), "r" (addr), "i" (-EFAULT), "0" (err)) + +#define __get_user_nocheck(x,ptr,size) \ +({ \ + long __gu_err, __gu_val; \ + __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +#define __get_user_check(x,ptr,size) \ +({ \ + long __gu_err = -EFAULT, __gu_val = 0; \ + const __typeof__(*(ptr)) *__gu_addr = (ptr); \ + if (access_ok(VERIFY_READ,__gu_addr,size)) \ + __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +extern long __get_user_bad(void); + +#define __get_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __get_user_asm(x,ptr,retval,1,"l8ui"); break; \ + case 2: __get_user_asm(x,ptr,retval,2,"l16ui"); break; \ + case 4: __get_user_asm(x,ptr,retval,4,"l32i"); break; \ + case 8: retval = __copy_from_user(&x,ptr,8); break; \ + default: (x) = __get_user_bad(); \ + } \ +} while (0) + + +/* + * WARNING: If you modify this macro at all, verify that the + * __check_align_* macros still work. + */ +#define __get_user_asm(x, addr, err, align, insn) \ + __asm__ __volatile__( \ + __check_align_##align \ + "1: "insn" %1, %2, 0 \n" \ + "2: \n" \ + " .section .fixup,\"ax\" \n" \ + " .align 4 \n" \ + "4: \n" \ + " .long 2b \n" \ + "5: \n" \ + " l32r %2, 4b \n" \ + " movi %1, 0 \n" \ + " movi %0, %3 \n" \ + " jx %2 \n" \ + " .previous \n" \ + " .section __ex_table,\"a\" \n" \ + " .long 1b, 5b \n" \ + " .previous" \ + :"=r" (err), "=r" (x) \ + :"r" (addr), "i" (-EFAULT), "0" (err)) + + +/* + * Copy to/from user space + */ + +/* + * We use a generic, arbitrary-sized copy subroutine. The Xtensa + * architecture would cause heavy code bloat if we tried to inline + * these functions and provide __constant_copy_* equivalents like the + * i386 versions. __xtensa_copy_user is quite efficient. See the + * .fixup section of __xtensa_copy_user for a discussion on the + * X_zeroing equivalents for Xtensa. + */ + +extern unsigned __xtensa_copy_user(void *to, const void *from, unsigned n); +#define __copy_user(to,from,size) __xtensa_copy_user(to,from,size) + + +static inline unsigned long +__generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n) +{ + return __copy_user(to,from,n); +} + +static inline unsigned long +__generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n) +{ + return __copy_user(to,from,n); +} + +static inline unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + prefetch(from); + if (access_ok(VERIFY_WRITE, to, n)) + return __copy_user(to,from,n); + return n; +} + +static inline unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + prefetchw(to); + if (access_ok(VERIFY_READ, from, n)) + return __copy_user(to,from,n); + else + memset(to, 0, n); + return n; +} + +#define copy_to_user(to,from,n) __generic_copy_to_user((to),(from),(n)) +#define copy_from_user(to,from,n) __generic_copy_from_user((to),(from),(n)) +#define __copy_to_user(to,from,n) __generic_copy_to_user_nocheck((to),(from),(n)) +#define __copy_from_user(to,from,n) __generic_copy_from_user_nocheck((to),(from),(n)) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + + +/* + * We need to return the number of bytes not cleared. Our memset() + * returns zero if a problem occurs while accessing user-space memory. + * In that event, return no memory cleared. Otherwise, zero for + * success. + */ + +extern inline unsigned long +__xtensa_clear_user(void *addr, unsigned long size) +{ + if ( ! memset(addr, 0, size) ) + return size; + return 0; +} + +extern inline unsigned long +clear_user(void *addr, unsigned long size) +{ + if (access_ok(VERIFY_WRITE, addr, size)) + return __xtensa_clear_user(addr, size); + return size ? -EFAULT : 0; +} + +#define __clear_user __xtensa_clear_user + + +extern long __strncpy_user(char *, const char *, long); +#define __strncpy_from_user __strncpy_user + +extern inline long +strncpy_from_user(char *dst, const char *src, long count) +{ + if (access_ok(VERIFY_READ, src, 1)) + return __strncpy_from_user(dst, src, count); + return -EFAULT; +} + + +#define strlen_user(str) strnlen_user((str), TASK_SIZE - 1) + +/* + * Return the size of a string (including the ending 0!) + */ +extern long __strnlen_user(const char *, long); + +extern inline long strnlen_user(const char *str, long len) +{ + unsigned long top = __kernel_ok ? ~0UL : TASK_SIZE - 1; + + if ((unsigned long)str > top) + return 0; + return __strnlen_user(str, len); +} + + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +/* Returns 0 if exception not found and fixup.unit otherwise. */ + +extern unsigned long search_exception_table(unsigned long addr); +extern void sort_exception_table(void); + +/* Returns the new pc */ +#define fixup_exception(map_reg, fixup_unit, pc) \ +({ \ + fixup_unit; \ +}) + +#endif /* __ASSEMBLY__ */ +#endif /* _XTENSA_UACCESS_H */ diff --git a/include/asm-xtensa/ucontext.h b/include/asm-xtensa/ucontext.h new file mode 100644 index 000000000000..94c94ed3e00a --- /dev/null +++ b/include/asm-xtensa/ucontext.h @@ -0,0 +1,22 @@ +/* + * include/asm-xtensa/ucontext.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_UCONTEXT_H +#define _XTENSA_UCONTEXT_H + +struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif /* _XTENSA_UCONTEXT_H */ diff --git a/include/asm-xtensa/unaligned.h b/include/asm-xtensa/unaligned.h new file mode 100644 index 000000000000..28220890d0a6 --- /dev/null +++ b/include/asm-xtensa/unaligned.h @@ -0,0 +1,28 @@ +/* + * include/asm-xtensa/unaligned.h + * + * Xtensa doesn't handle unaligned accesses efficiently. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_UNALIGNED_H +#define _XTENSA_UNALIGNED_H + +#include + +/* Use memmove here, so gcc does not insert a __builtin_memcpy. */ + +#define get_unaligned(ptr) \ + ({ __typeof__(*(ptr)) __tmp; memmove(&__tmp, (ptr), sizeof(*(ptr))); __tmp; }) + +#define put_unaligned(val, ptr) \ + ({ __typeof__(*(ptr)) __tmp = (val); \ + memmove((ptr), &__tmp, sizeof(*(ptr))); \ + (void)0; }) + +#endif /* _XTENSA_UNALIGNED_H */ diff --git a/include/asm-xtensa/unistd.h b/include/asm-xtensa/unistd.h new file mode 100644 index 000000000000..64c64dd83ba4 --- /dev/null +++ b/include/asm-xtensa/unistd.h @@ -0,0 +1,537 @@ +/* + * include/asm-xtensa/unistd.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_UNISTD_H +#define _XTENSA_UNISTD_H + +#include + +//#define __NR_setup 0 /* used only by init, to get system going */ +#define __NR_spill 0 +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_break 17 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_oldumount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_profil 98 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_modify_ldt 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_cacheflush 147 +#define __NR_cachectl 148 +#define __NR_sysxtensa 149 +#define __NR_sysdummy 150 +#define __NR_getsid 151 +#define __NR_fdatasync 152 +#define __NR__sysctl 153 +#define __NR_mlock 154 +#define __NR_munlock 155 +#define __NR_mlockall 156 +#define __NR_munlockall 157 +#define __NR_sched_setparam 158 +#define __NR_sched_getparam 159 +#define __NR_sched_setscheduler 160 +#define __NR_sched_getscheduler 161 +#define __NR_sched_yield 162 +#define __NR_sched_get_priority_max 163 +#define __NR_sched_get_priority_min 164 +#define __NR_sched_rr_get_interval 165 +#define __NR_nanosleep 166 +#define __NR_mremap 167 +#define __NR_accept 168 +#define __NR_bind 169 +#define __NR_connect 170 +#define __NR_getpeername 171 +#define __NR_getsockname 172 +#define __NR_getsockopt 173 +#define __NR_listen 174 +#define __NR_recv 175 +#define __NR_recvfrom 176 +#define __NR_recvmsg 177 +#define __NR_send 178 +#define __NR_sendmsg 179 +#define __NR_sendto 180 +#define __NR_setsockopt 181 +#define __NR_shutdown 182 +#define __NR_socket 183 +#define __NR_socketpair 184 +#define __NR_setresuid 185 +#define __NR_getresuid 186 +#define __NR_query_module 187 +#define __NR_poll 188 +#define __NR_nfsservctl 189 +#define __NR_setresgid 190 +#define __NR_getresgid 191 +#define __NR_prctl 192 +#define __NR_rt_sigreturn 193 +#define __NR_rt_sigaction 194 +#define __NR_rt_sigprocmask 195 +#define __NR_rt_sigpending 196 +#define __NR_rt_sigtimedwait 197 +#define __NR_rt_sigqueueinfo 198 +#define __NR_rt_sigsuspend 199 +#define __NR_pread 200 +#define __NR_pwrite 201 +#define __NR_chown 202 +#define __NR_getcwd 203 +#define __NR_capget 204 +#define __NR_capset 205 +#define __NR_sigaltstack 206 +#define __NR_sendfile 207 +#define __NR_streams1 208 /* some people actually want it */ +#define __NR_streams2 209 /* some people actually want it */ +#define __NR_mmap2 210 +#define __NR_truncate64 211 +#define __NR_ftruncate64 212 +#define __NR_stat64 213 +#define __NR_lstat64 214 +#define __NR_fstat64 215 +#define __NR_pivot_root 216 +#define __NR_mincore 217 +#define __NR_madvise 218 +#define __NR_getdents64 219 +#define __NR_vfork 220 + +/* Keep this last; should always equal the last valid call number. */ +#define __NR_Linux_syscalls 220 + +/* user-visible error numbers are in the range -1 - -125: see + * */ + +#define SYSXTENSA_RESERVED 0 /* don't use this */ +#define SYSXTENSA_ATOMIC_SET 1 /* set variable */ +#define SYSXTENSA_ATOMIC_EXG_ADD 2 /* exchange memory and add */ +#define SYSXTENSA_ATOMIC_ADD 3 /* add to memory */ +#define SYSXTENSA_ATOMIC_CMP_SWP 4 /* compare and swap */ + +#define SYSXTENSA_COUNT 5 /* count of syscall0 functions*/ + +#ifdef __KERNEL__ +#define __syscall_return(type, res) return ((type)(res)) +#else +#define __syscall_return(type, res) \ +do { \ + if ((unsigned long)(res) >= (unsigned long)(-125)) { \ + /* Avoid using "res" which is declared to be in register r2; \ + * errno might expand to a function call and clobber it. */ \ + int __err = -(res); \ + errno = __err; \ + res = -1; \ + } \ + return (type) (res); \ +} while (0) +#endif + + +/* Tensilica's xt-xcc compiler is much more agressive at code + * optimization than gcc. Multiple __asm__ statements are + * insufficient for xt-xcc because subsequent optimization passes + * (beyond the front-end that knows of __asm__ statements and other + * such GNU Extensions to C) can modify the register selection for + * containment of C variables. + * + * xt-xcc cannot modify the contents of a single __asm__ statement, so + * we create single-asm versions of the syscall macros that are + * suitable and optimal for both xt-xcc and gcc. + * + * Linux takes system-call arguments in registers. The following + * design is optimized for user-land apps (e.g., glibc) which + * typically have a function wrapper around the "syscall" assembly + * instruction. It satisfies the Xtensa ABI while minizing argument + * shifting. + * + * The Xtensa ABI and software conventions require the system-call + * number in a2. If an argument exists in a2, we move it to the next + * available register. Note that for improved efficiency, we do NOT + * shift all parameters down one register to maintain the original + * order. + * + * At best case (zero arguments), we just write the syscall number to + * a2. At worst case (1 to 6 arguments), we move the argument in a2 + * to the next available register, then write the syscall number to + * a2. + * + * For clarity, the following truth table enumerates all possibilities. + * + * arguments syscall number arg0, arg1, arg2, arg3, arg4, arg5 + * --------- -------------- ---------------------------------- + * 0 a2 + * 1 a2 a3 + * 2 a2 a4, a3 + * 3 a2 a5, a3, a4 + * 4 a2 a6, a3, a4, a5 + * 5 a2 a7, a3, a4, a5, a6 + * 6 a2 a8, a3, a4, a5, a6, a7 + */ + +#define _syscall0(type,name) \ +type name(void) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name) \ + : "a2" \ + ); \ +__syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type0,arg0) \ +type name(type0 arg0) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " mov a3, %2 \n" \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name), "a" (arg0) \ + : "a2", "a3" \ + ); \ +__syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type0,arg0,type1,arg1) \ +type name(type0 arg0,type1 arg1) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " mov a4, %2 \n" \ + " mov a3, %3 \n" \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name), "a" (arg0), "a" (arg1) \ + : "a2", "a3", "a4" \ + ); \ +__syscall_return(type,__res); \ +} + +#define _syscall3(type,name,type0,arg0,type1,arg1,type2,arg2) \ +type name(type0 arg0,type1 arg1,type2 arg2) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " mov a5, %2 \n" \ + " mov a4, %4 \n" \ + " mov a3, %3 \n" \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name), "a" (arg0), "a" (arg1), "a" (arg2) \ + : "a2", "a3", "a4", "a5" \ + ); \ +__syscall_return(type,__res); \ +} + +#define _syscall4(type,name,type0,arg0,type1,arg1,type2,arg2,type3,arg3) \ +type name(type0 arg0,type1 arg1,type2 arg2,type3 arg3) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " mov a6, %2 \n" \ + " mov a5, %5 \n" \ + " mov a4, %4 \n" \ + " mov a3, %3 \n" \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name), "a" (arg0), "a" (arg1), "a" (arg2), "a" (arg3) \ + : "a2", "a3", "a4", "a5", "a6" \ + ); \ +__syscall_return(type,__res); \ +} + +/* Note that we save and restore the a7 frame pointer. + * Including a7 in the clobber list doesn't do what you'd expect. + */ +#define _syscall5(type,name,type0,arg0,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type name(type0 arg0,type1 arg1,type2 arg2,type3 arg3,type4 arg4) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " mov a9, a7 \n" \ + " mov a7, %2 \n" \ + " mov a6, %6 \n" \ + " mov a5, %5 \n" \ + " mov a4, %4 \n" \ + " mov a3, %3 \n" \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov a7, a9 \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name), "a" (arg0), "a" (arg1), "a" (arg2), \ + "a" (arg3), "a" (arg4) \ + : "a2", "a3", "a4", "a5", "a6", "a9" \ + ); \ +__syscall_return(type,__res); \ +} + +/* Note that we save and restore the a7 frame pointer. + * Including a7 in the clobber list doesn't do what you'd expect. + */ +#define _syscall6(type,name,type0,arg0,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \ +type name(type0 arg0,type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ +{ \ +long __res; \ +__asm__ __volatile__ ( \ + " mov a9, a7 \n" \ + " mov a8, %2 \n" \ + " mov a7, %7 \n" \ + " mov a6, %6 \n" \ + " mov a5, %5 \n" \ + " mov a4, %4 \n" \ + " mov a3, %3 \n" \ + " movi a2, %1 \n" \ + " syscall \n" \ + " mov a7, a9 \n" \ + " mov %0, a2 \n" \ + : "=a" (__res) \ + : "i" (__NR_##name), "a" (arg0), "a" (arg1), "a" (arg2), \ + "a" (arg3), "a" (arg4), "a" (arg5) \ + : "a2", "a3", "a4", "a5", "a6", "a8", "a9" \ + ); \ +__syscall_return(type,__res); \ +} + + +#ifdef __KERNEL_SYSCALLS__ + +#include +#include +#include + +/* + * we need this inline - forking from kernel space will result + * in NO COPY ON WRITE (!!!), until an execve is executed. This + * is no problem, but for the stack. This is handled by not letting + * main() use the stack at all after fork(). Thus, no function + * calls - which means inline code for fork too, as otherwise we + * would use the stack upon exit from 'fork()'. + * + * Actually only pause and fork are needed inline, so that there + * won't be any messing with the stack from main(), but we define + * some others too. + */ + +#define __NR__exit __NR_exit + +static __inline__ _syscall0(int,pause) +//static __inline__ _syscall1(int,setup,int,magic) FIXME +static __inline__ _syscall0(int,sync) +static __inline__ _syscall0(pid_t,setsid) +static __inline__ _syscall3(int,write,int,fd,const char *,buf,off_t,count) +static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count) +static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) +static __inline__ _syscall1(int,dup,int,fd) +static __inline__ _syscall3(int,execve,const char*,file,char**,argv,char**,envp) +static __inline__ _syscall3(int,open,const char *,file,int,flag,int,mode) +static __inline__ _syscall1(int,close,int,fd) +static __inline__ _syscall1(int,_exit,int,exitcode) +static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static __inline__ _syscall1(int,delete_module,const char *,name) + +struct stat; +static __inline__ _syscall2(int,fstat,int,fd,struct stat *,buf) +static __inline__ _syscall0(pid_t,getpid) +static __inline__ _syscall2(int,kill,int,pid,int,sig) +static __inline__ _syscall2(int,stat,const char *, path,struct stat *,buf) +static __inline__ _syscall1(int,unlink,char *,pathname) + + + +extern pid_t waitpid(int, int*, int ); +static __inline__ pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} +#endif + +/* + * "Conditional" syscalls + * + * What we want is __attribute__((weak,alias("sys_ni_syscall"))), + * but it doesn't work on all toolchains, so we just do it by hand + */ +#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); + +#ifdef __KERNEL__ +#define __ARCH_WANT_IPC_PARSE_VERSION +#define __ARCH_WANT_OLD_READDIR +#define __ARCH_WANT_OLD_STAT +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SYS_ALARM +#define __ARCH_WANT_SYS_GETHOSTNAME +#define __ARCH_WANT_SYS_PAUSE +#define __ARCH_WANT_SYS_SGETMASK +#define __ARCH_WANT_SYS_SIGNAL +#define __ARCH_WANT_SYS_TIME +#define __ARCH_WANT_SYS_UTIME +#define __ARCH_WANT_SYS_WAITPID +#define __ARCH_WANT_SYS_SOCKETCALL +#define __ARCH_WANT_SYS_FADVISE64 +#define __ARCH_WANT_SYS_GETPGRP +#define __ARCH_WANT_SYS_LLSEEK +#define __ARCH_WANT_SYS_NICE +#define __ARCH_WANT_SYS_OLD_GETRLIMIT +#define __ARCH_WANT_SYS_OLDUMOUNT +#define __ARCH_WANT_SYS_SIGPENDING +#define __ARCH_WANT_SYS_SIGPROCMASK +#define __ARCH_WANT_SYS_RT_SIGACTION +#endif + + + +#endif /* _XTENSA_UNISTD_H */ diff --git a/include/asm-xtensa/user.h b/include/asm-xtensa/user.h new file mode 100644 index 000000000000..2c3ed23354a8 --- /dev/null +++ b/include/asm-xtensa/user.h @@ -0,0 +1,20 @@ +/* + * include/asm-xtensa/user.h + * + * Xtensa Processor version. + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_USER_H +#define _XTENSA_USER_H + +/* This file usually defines a 'struct user' structure. However, it it only + * used for a.out file, which are not supported on Xtensa. + */ + +#endif /* _XTENSA_USER_H */ diff --git a/include/asm-xtensa/vga.h b/include/asm-xtensa/vga.h new file mode 100644 index 000000000000..23d82f6acb57 --- /dev/null +++ b/include/asm-xtensa/vga.h @@ -0,0 +1,19 @@ +/* + * include/asm-xtensa/vga.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_VGA_H +#define _XTENSA_VGA_H + +#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) + +#define vga_readb(x) (*(x)) +#define vga_writeb(x,y) (*(y) = (x)) + +#endif diff --git a/include/asm-xtensa/xor.h b/include/asm-xtensa/xor.h new file mode 100644 index 000000000000..e7b1f083991d --- /dev/null +++ b/include/asm-xtensa/xor.h @@ -0,0 +1,16 @@ +/* + * include/asm-xtensa/xor.h + * + * 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) 2001 - 2005 Tensilica Inc. + */ + +#ifndef _XTENSA_XOR_H +#define _XTENSA_XOR_H + +#include + +#endif diff --git a/include/asm-xtensa/xtensa/cacheasm.h b/include/asm-xtensa/xtensa/cacheasm.h new file mode 100644 index 000000000000..0cdbb0bf180e --- /dev/null +++ b/include/asm-xtensa/xtensa/cacheasm.h @@ -0,0 +1,708 @@ +#ifndef XTENSA_CACHEASM_H +#define XTENSA_CACHEASM_H + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * include/asm-xtensa/xtensa/cacheasm.h -- assembler-specific cache + * related definitions that depend on CORE configuration. + * + * 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) 2002 Tensilica Inc. + */ + + +#include + + +/* + * This header file defines assembler macros of the form: + * cache_ + * where is 'i' or 'd' for instruction and data caches, + * and indicates the function of the macro. + * + * The following functions are defined, + * and apply only to the specified cache (I or D): + * + * reset + * Resets the cache. + * + * sync + * Makes sure any previous cache instructions have been completed; + * ie. makes sure any previous cache control operations + * have had full effect and been synchronized to memory. + * Eg. any invalidate completed [so as not to generate a hit], + * any writebacks or other pipelined writes written to memory, etc. + * + * invalidate_line (single cache line) + * invalidate_region (specified memory range) + * invalidate_all (entire cache) + * Invalidates all cache entries that cache + * data from the specified memory range. + * NOTE: locked entries are not invalidated. + * + * writeback_line (single cache line) + * writeback_region (specified memory range) + * writeback_all (entire cache) + * Writes back to memory all dirty cache entries + * that cache data from the specified memory range, + * and marks these entries as clean. + * NOTE: on some future implementations, this might + * also invalidate. + * NOTE: locked entries are written back, but never invalidated. + * NOTE: instruction caches never implement writeback. + * + * writeback_inv_line (single cache line) + * writeback_inv_region (specified memory range) + * writeback_inv_all (entire cache) + * Writes back to memory all dirty cache entries + * that cache data from the specified memory range, + * and invalidates these entries (including all clean + * cache entries that cache data from that range). + * NOTE: locked entries are written back but not invalidated. + * NOTE: instruction caches never implement writeback. + * + * lock_line (single cache line) + * lock_region (specified memory range) + * Prefetch and lock the specified memory range into cache. + * NOTE: if any part of the specified memory range cannot + * be locked, a ??? exception occurs. These macros don't + * do anything special (yet anyway) to handle this situation. + * + * unlock_line (single cache line) + * unlock_region (specified memory range) + * unlock_all (entire cache) + * Unlock cache entries that cache the specified memory range. + * Entries not already locked are unaffected. + */ + + + +/*************************** GENERIC -- ALL CACHES ***************************/ + + +/* + * The following macros assume the following cache size/parameter limits + * in the current Xtensa core implementation: + * cache size: 1024 bytes minimum + * line size: 16 - 64 bytes + * way count: 1 - 4 + * + * Minimum entries per way (ie. per associativity) = 1024 / 64 / 4 = 4 + * Hence the assumption that each loop can execute four cache instructions. + * + * Correspondingly, the offset range of instructions is assumed able to cover + * four lines, ie. offsets {0,1,2,3} * line_size are assumed valid for + * both hit and indexed cache instructions. Ie. these offsets are all + * valid: 0, 16, 32, 48, 64, 96, 128, 192 (for line sizes 16, 32, 64). + * This is true of all original cache instructions + * (dhi, ihi, dhwb, dhwbi, dii, iii) which have offsets + * of 0 to 1020 in multiples of 4 (ie. 8 bits shifted by 2). + * This is also true of subsequent cache instructions + * (dhu, ihu, diu, iiu, diwb, diwbi, dpfl, ipfl) which have offsets + * of 0 to 240 in multiples of 16 (ie. 4 bits shifted by 4). + * + * (Maximum cache size, currently 32k, doesn't affect the following macros. + * Cache ways > MMU min page size cause aliasing but that's another matter.) + */ + + + +/* + * Macro to apply an 'indexed' cache instruction to the entire cache. + * + * Parameters: + * cainst instruction/ that takes an address register parameter + * and an offset parameter (in range 0 .. 3*linesize). + * size size of cache in bytes + * linesize size of cache line in bytes + * assoc_or1 number of associativities (ways/sets) in cache + * if all sets affected by cainst, + * or 1 if only one set (or not all sets) of the cache + * is affected by cainst (eg. DIWB or DIWBI [not yet ISA defined]). + * aa, ab unique address registers (temporaries) + */ + + .macro cache_index_all cainst, size, linesize, assoc_or1, aa, ab + + // Sanity-check on cache parameters: + .ifne (\size % (\linesize * \assoc_or1 * 4)) + .err // cache configuration outside expected/supported range! + .endif + + // \size byte cache, \linesize byte lines, \assoc_or1 way(s) affected by each \cainst. + movi \aa, (\size / (\linesize * \assoc_or1 * 4)) + // Possible improvement: need only loop if \aa > 1 ; + // however that particular condition is highly unlikely. + movi \ab, 0 // to iterate over cache + floop \aa, cachex\@ + \cainst \ab, 0*\linesize + \cainst \ab, 1*\linesize + \cainst \ab, 2*\linesize + \cainst \ab, 3*\linesize + addi \ab, \ab, 4*\linesize // move to next line + floopend \aa, cachex\@ + + .endm + + +/* + * Macro to apply a 'hit' cache instruction to a memory region, + * ie. to any cache entries that cache a specified portion (region) of memory. + * Takes care of the unaligned cases, ie. may apply to one + * more cache line than $asize / lineSize if $aaddr is not aligned. + * + * + * Parameters are: + * cainst instruction/macro that takes an address register parameter + * and an offset parameter (currently always zero) + * and generates a cache instruction (eg. "dhi", "dhwb", "ihi", etc.) + * linesize_log2 log2(size of cache line in bytes) + * addr register containing start address of region (clobbered) + * asize register containing size of the region in bytes (clobbered) + * askew unique register used as temporary + * + * !?!?! 2DO: optimization: iterate max(cache_size and \asize) / linesize + */ + + .macro cache_hit_region cainst, linesize_log2, addr, asize, askew + + // Make \asize the number of iterations: + extui \askew, \addr, 0, \linesize_log2 // get unalignment amount of \addr + add \asize, \asize, \askew // ... and add it to \asize + addi \asize, \asize, (1 << \linesize_log2) - 1 // round up! + srli \asize, \asize, \linesize_log2 + + // Iterate over region: + floopnez \asize, cacheh\@ + \cainst \addr, 0 + addi \addr, \addr, (1 << \linesize_log2) // move to next line + floopend \asize, cacheh\@ + + .endm + + + + + +/*************************** INSTRUCTION CACHE ***************************/ + + +/* + * Reset/initialize the instruction cache by simply invalidating it: + * (need to unlock first also, if cache locking implemented): + * + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro icache_reset aa, ab + icache_unlock_all \aa, \ab + icache_invalidate_all \aa, \ab + .endm + + +/* + * Synchronize after an instruction cache operation, + * to be sure everything is in sync with memory as to be + * expected following any previous instruction cache control operations. + * + * Parameters are: + * ar an address register (temporary) (currently unused, but may be used in future) + */ + .macro icache_sync ar +#if XCHAL_ICACHE_SIZE > 0 + isync +#endif + .endm + + + +/* + * Invalidate a single line of the instruction cache. + * Parameters are: + * ar address register that contains (virtual) address to invalidate + * (may get clobbered in a future implementation, but not currently) + * offset (optional) offset to add to \ar to compute effective address to invalidate + * (note: some number of lsbits are ignored) + */ + .macro icache_invalidate_line ar, offset +#if XCHAL_ICACHE_SIZE > 0 + ihi \ar, \offset // invalidate icache line + /* + * NOTE: in some version of the silicon [!!!SHOULD HAVE BEEN DOCUMENTED!!!] + * 'ihi' doesn't work, so it had been replaced with 'iii' + * (which would just invalidate more than it should, + * which should be okay other than the performance hit + * because cache locking did not exist in that version, + * unless user somehow relies on something being cached). + * [WHAT VERSION IS IT!!?!? + * IS THERE ANY WAY TO TEST FOR THAT HERE, TO OUTPUT 'III' ONLY IF NEEDED!?!?]. + * + * iii \ar, \offset + */ + icache_sync \ar +#endif + .endm + + + + +/* + * Invalidate instruction cache entries that cache a specified portion of memory. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro icache_invalidate_region astart, asize, ac +#if XCHAL_ICACHE_SIZE > 0 + // Instruction cache region invalidation: + cache_hit_region ihi, XCHAL_ICACHE_LINEWIDTH, \astart, \asize, \ac + icache_sync \ac + // End of instruction cache region invalidation +#endif + .endm + + + +/* + * Invalidate entire instruction cache. + * + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro icache_invalidate_all aa, ab +#if XCHAL_ICACHE_SIZE > 0 + // Instruction cache invalidation: + cache_index_all iii, XCHAL_ICACHE_SIZE, XCHAL_ICACHE_LINESIZE, XCHAL_ICACHE_WAYS, \aa, \ab + icache_sync \aa + // End of instruction cache invalidation +#endif + .endm + + + +/* + * Lock (prefetch & lock) a single line of the instruction cache. + * + * Parameters are: + * ar address register that contains (virtual) address to lock + * (may get clobbered in a future implementation, but not currently) + * offset offset to add to \ar to compute effective address to lock + * (note: some number of lsbits are ignored) + */ + .macro icache_lock_line ar, offset +#if XCHAL_ICACHE_SIZE > 0 && XCHAL_ICACHE_LINE_LOCKABLE + ipfl \ar, \offset /* prefetch and lock icache line */ + icache_sync \ar +#endif + .endm + + + +/* + * Lock (prefetch & lock) a specified portion of memory into the instruction cache. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro icache_lock_region astart, asize, ac +#if XCHAL_ICACHE_SIZE > 0 && XCHAL_ICACHE_LINE_LOCKABLE + // Instruction cache region lock: + cache_hit_region ipfl, XCHAL_ICACHE_LINEWIDTH, \astart, \asize, \ac + icache_sync \ac + // End of instruction cache region lock +#endif + .endm + + + +/* + * Unlock a single line of the instruction cache. + * + * Parameters are: + * ar address register that contains (virtual) address to unlock + * (may get clobbered in a future implementation, but not currently) + * offset offset to add to \ar to compute effective address to unlock + * (note: some number of lsbits are ignored) + */ + .macro icache_unlock_line ar, offset +#if XCHAL_ICACHE_SIZE > 0 && XCHAL_ICACHE_LINE_LOCKABLE + ihu \ar, \offset /* unlock icache line */ + icache_sync \ar +#endif + .endm + + + +/* + * Unlock a specified portion of memory from the instruction cache. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro icache_unlock_region astart, asize, ac +#if XCHAL_ICACHE_SIZE > 0 && XCHAL_ICACHE_LINE_LOCKABLE + // Instruction cache region unlock: + cache_hit_region ihu, XCHAL_ICACHE_LINEWIDTH, \astart, \asize, \ac + icache_sync \ac + // End of instruction cache region unlock +#endif + .endm + + + +/* + * Unlock entire instruction cache. + * + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro icache_unlock_all aa, ab +#if XCHAL_ICACHE_SIZE > 0 && XCHAL_ICACHE_LINE_LOCKABLE + // Instruction cache unlock: + cache_index_all iiu, XCHAL_ICACHE_SIZE, XCHAL_ICACHE_LINESIZE, 1, \aa, \ab + icache_sync \aa + // End of instruction cache unlock +#endif + .endm + + + + + +/*************************** DATA CACHE ***************************/ + + + +/* + * Reset/initialize the data cache by simply invalidating it + * (need to unlock first also, if cache locking implemented): + * + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro dcache_reset aa, ab + dcache_unlock_all \aa, \ab + dcache_invalidate_all \aa, \ab + .endm + + + + +/* + * Synchronize after a data cache operation, + * to be sure everything is in sync with memory as to be + * expected following any previous data cache control operations. + * + * Parameters are: + * ar an address register (temporary) (currently unused, but may be used in future) + */ + .macro dcache_sync ar +#if XCHAL_DCACHE_SIZE > 0 + // This previous sequence errs on the conservative side (too much so); a DSYNC should be sufficient: + //memw // synchronize data cache changes relative to subsequent memory accesses + //isync // be conservative and ISYNC as well (just to be sure) + + dsync +#endif + .endm + + + +/* + * Synchronize after a data store operation, + * to be sure the stored data is completely off the processor + * (and assuming there is no buffering outside the processor, + * that the data is in memory). This may be required to + * ensure that the processor's write buffers are emptied. + * A MEMW followed by a read guarantees this, by definition. + * We also try to make sure the read itself completes. + * + * Parameters are: + * ar an address register (temporary) + */ + .macro write_sync ar + memw // ensure previous memory accesses are complete prior to subsequent memory accesses + l32i \ar, sp, 0 // completing this read ensures any previous write has completed, because of MEMW + //slot + add \ar, \ar, \ar // use the result of the read to help ensure the read completes (in future architectures) + .endm + + +/* + * Invalidate a single line of the data cache. + * Parameters are: + * ar address register that contains (virtual) address to invalidate + * (may get clobbered in a future implementation, but not currently) + * offset (optional) offset to add to \ar to compute effective address to invalidate + * (note: some number of lsbits are ignored) + */ + .macro dcache_invalidate_line ar, offset +#if XCHAL_DCACHE_SIZE > 0 + dhi \ar, \offset + dcache_sync \ar +#endif + .endm + + + + + +/* + * Invalidate data cache entries that cache a specified portion of memory. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro dcache_invalidate_region astart, asize, ac +#if XCHAL_DCACHE_SIZE > 0 + // Data cache region invalidation: + cache_hit_region dhi, XCHAL_DCACHE_LINEWIDTH, \astart, \asize, \ac + dcache_sync \ac + // End of data cache region invalidation +#endif + .endm + + + +#if 0 +/* + * This is a work-around for a bug in SiChip1 (???). + * There should be a proper mechanism for not outputting + * these instructions when not needed. + * To enable work-around, uncomment this and replace 'dii' + * with 'dii_s1' everywhere, eg. in dcache_invalidate_all + * macro below. + */ + .macro dii_s1 ar, offset + dii \ar, \offset + or \ar, \ar, \ar + or \ar, \ar, \ar + or \ar, \ar, \ar + or \ar, \ar, \ar + .endm +#endif + + +/* + * Invalidate entire data cache. + * + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro dcache_invalidate_all aa, ab +#if XCHAL_DCACHE_SIZE > 0 + // Data cache invalidation: + cache_index_all dii, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE, XCHAL_DCACHE_WAYS, \aa, \ab + dcache_sync \aa + // End of data cache invalidation +#endif + .endm + + + +/* + * Writeback a single line of the data cache. + * Parameters are: + * ar address register that contains (virtual) address to writeback + * (may get clobbered in a future implementation, but not currently) + * offset offset to add to \ar to compute effective address to writeback + * (note: some number of lsbits are ignored) + */ + .macro dcache_writeback_line ar, offset +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_IS_WRITEBACK + dhwb \ar, \offset + dcache_sync \ar +#endif + .endm + + + +/* + * Writeback dirty data cache entries that cache a specified portion of memory. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro dcache_writeback_region astart, asize, ac +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_IS_WRITEBACK + // Data cache region writeback: + cache_hit_region dhwb, XCHAL_DCACHE_LINEWIDTH, \astart, \asize, \ac + dcache_sync \ac + // End of data cache region writeback +#endif + .endm + + + +/* + * Writeback entire data cache. + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro dcache_writeback_all aa, ab +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_IS_WRITEBACK + // Data cache writeback: + cache_index_all diwb, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE, 1, \aa, \ab + dcache_sync \aa + // End of data cache writeback +#endif + .endm + + + +/* + * Writeback and invalidate a single line of the data cache. + * Parameters are: + * ar address register that contains (virtual) address to writeback and invalidate + * (may get clobbered in a future implementation, but not currently) + * offset offset to add to \ar to compute effective address to writeback and invalidate + * (note: some number of lsbits are ignored) + */ + .macro dcache_writeback_inv_line ar, offset +#if XCHAL_DCACHE_SIZE > 0 + dhwbi \ar, \offset /* writeback and invalidate dcache line */ + dcache_sync \ar +#endif + .endm + + + +/* + * Writeback and invalidate data cache entries that cache a specified portion of memory. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro dcache_writeback_inv_region astart, asize, ac +#if XCHAL_DCACHE_SIZE > 0 + // Data cache region writeback and invalidate: + cache_hit_region dhwbi, XCHAL_DCACHE_LINEWIDTH, \astart, \asize, \ac + dcache_sync \ac + // End of data cache region writeback and invalidate +#endif + .endm + + + +/* + * Writeback and invalidate entire data cache. + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro dcache_writeback_inv_all aa, ab +#if XCHAL_DCACHE_SIZE > 0 + // Data cache writeback and invalidate: +#if XCHAL_DCACHE_IS_WRITEBACK + cache_index_all diwbi, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE, 1, \aa, \ab + dcache_sync \aa +#else /*writeback*/ + // Data cache does not support writeback, so just invalidate: */ + dcache_invalidate_all \aa, \ab +#endif /*writeback*/ + // End of data cache writeback and invalidate +#endif + .endm + + + + +/* + * Lock (prefetch & lock) a single line of the data cache. + * + * Parameters are: + * ar address register that contains (virtual) address to lock + * (may get clobbered in a future implementation, but not currently) + * offset offset to add to \ar to compute effective address to lock + * (note: some number of lsbits are ignored) + */ + .macro dcache_lock_line ar, offset +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_LINE_LOCKABLE + dpfl \ar, \offset /* prefetch and lock dcache line */ + dcache_sync \ar +#endif + .endm + + + +/* + * Lock (prefetch & lock) a specified portion of memory into the data cache. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro dcache_lock_region astart, asize, ac +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_LINE_LOCKABLE + // Data cache region lock: + cache_hit_region dpfl, XCHAL_DCACHE_LINEWIDTH, \astart, \asize, \ac + dcache_sync \ac + // End of data cache region lock +#endif + .endm + + + +/* + * Unlock a single line of the data cache. + * + * Parameters are: + * ar address register that contains (virtual) address to unlock + * (may get clobbered in a future implementation, but not currently) + * offset offset to add to \ar to compute effective address to unlock + * (note: some number of lsbits are ignored) + */ + .macro dcache_unlock_line ar, offset +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_LINE_LOCKABLE + dhu \ar, \offset /* unlock dcache line */ + dcache_sync \ar +#endif + .endm + + + +/* + * Unlock a specified portion of memory from the data cache. + * Parameters are: + * astart start address (register gets clobbered) + * asize size of the region in bytes (register gets clobbered) + * ac unique register used as temporary + */ + .macro dcache_unlock_region astart, asize, ac +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_LINE_LOCKABLE + // Data cache region unlock: + cache_hit_region dhu, XCHAL_DCACHE_LINEWIDTH, \astart, \asize, \ac + dcache_sync \ac + // End of data cache region unlock +#endif + .endm + + + +/* + * Unlock entire data cache. + * + * Parameters: + * aa, ab unique address registers (temporaries) + */ + .macro dcache_unlock_all aa, ab +#if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_LINE_LOCKABLE + // Data cache unlock: + cache_index_all diu, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE, 1, \aa, \ab + dcache_sync \aa + // End of data cache unlock +#endif + .endm + + +#endif /*XTENSA_CACHEASM_H*/ + diff --git a/include/asm-xtensa/xtensa/cacheattrasm.h b/include/asm-xtensa/xtensa/cacheattrasm.h new file mode 100644 index 000000000000..1c3e117b3592 --- /dev/null +++ b/include/asm-xtensa/xtensa/cacheattrasm.h @@ -0,0 +1,432 @@ +#ifndef XTENSA_CACHEATTRASM_H +#define XTENSA_CACHEATTRASM_H + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * include/asm-xtensa/xtensa/cacheattrasm.h -- assembler-specific + * CACHEATTR register related definitions that depend on CORE + * configuration. + * + * 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) 2002 Tensilica Inc. + */ + + +#include + + +/* + * This header file defines assembler macros of the form: + * cacheattr_ + * where: + * is 'i', 'd' or absent for instruction, data + * or both caches; and + * indicates the function of the macro. + * + * The following functions are defined: + * + * icacheattr_get + * Reads I-cache CACHEATTR into a2 (clobbers a3-a5). + * + * dcacheattr_get + * Reads D-cache CACHEATTR into a2 (clobbers a3-a5). + * (Note: for configs with a real CACHEATTR register, the + * above two macros are identical.) + * + * cacheattr_set + * Writes both I-cache and D-cache CACHEATTRs from a2 (a3-a8 clobbered). + * Works even when changing one's own code's attributes. + * + * icacheattr_is_enabled label + * Branches to \label if I-cache appears to have been enabled + * (eg. if CACHEATTR contains a cache-enabled attribute). + * (clobbers a2-a5,SAR) + * + * dcacheattr_is_enabled label + * Branches to \label if D-cache appears to have been enabled + * (eg. if CACHEATTR contains a cache-enabled attribute). + * (clobbers a2-a5,SAR) + * + * cacheattr_is_enabled label + * Branches to \label if either I-cache or D-cache appears to have been enabled + * (eg. if CACHEATTR contains a cache-enabled attribute). + * (clobbers a2-a5,SAR) + * + * The following macros are only defined under certain conditions: + * + * icacheattr_set (if XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR) + * Writes I-cache CACHEATTR from a2 (a3-a8 clobbered). + * + * dcacheattr_set (if XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR) + * Writes D-cache CACHEATTR from a2 (a3-a8 clobbered). + */ + + + +/*************************** GENERIC -- ALL CACHES ***************************/ + +/* + * _cacheattr_get + * + * (Internal macro.) + * Returns value of CACHEATTR register (or closest equivalent) in a2. + * + * Entry: + * (none) + * Exit: + * a2 value read from CACHEATTR + * a3-a5 clobbered (temporaries) + */ + .macro _cacheattr_get tlb +#if XCHAL_HAVE_CACHEATTR + rsr a2, CACHEATTR +#elif XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + // We have a config that "mimics" CACHEATTR using a simplified + // "MMU" composed of a single statically-mapped way. + // DTLB and ITLB are independent, so there's no single + // cache attribute that can describe both. So for now + // just return the DTLB state. + movi a5, 0xE0000000 + movi a2, 0 + movi a3, 0 +1: add a3, a3, a5 // next segment + r&tlb&1 a4, a3 // get PPN+CA of segment at 0xE0000000, 0xC0000000, ..., 0 + dsync // interlock??? + slli a2, a2, 4 + extui a4, a4, 0, 4 // extract CA + or a2, a2, a4 + bnez a3, 1b +#else + // This macro isn't applicable to arbitrary MMU configurations. + // Just return zero. + movi a2, 0 +#endif + .endm + + .macro icacheattr_get + _cacheattr_get itlb + .endm + + .macro dcacheattr_get + _cacheattr_get dtlb + .endm + + +#define XCHAL_CACHEATTR_ALL_BYPASS 0x22222222 /* default (powerup/reset) value of CACHEATTR, all BYPASS + mode (ie. disabled/bypassed caches) */ + +#if XCHAL_HAVE_CACHEATTR || XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + +#define XCHAL_FCA_ENAMASK 0x001A /* bitmap of fetch attributes that require enabled icache */ +#define XCHAL_LCA_ENAMASK 0x0003 /* bitmap of load attributes that require enabled dcache */ +#define XCHAL_SCA_ENAMASK 0x0003 /* bitmap of store attributes that require enabled dcache */ +#define XCHAL_LSCA_ENAMASK (XCHAL_LCA_ENAMASK|XCHAL_SCA_ENAMASK) /* l/s attrs requiring enabled dcache */ +#define XCHAL_ALLCA_ENAMASK (XCHAL_FCA_ENAMASK|XCHAL_LSCA_ENAMASK) /* all attrs requiring enabled caches */ + +/* + * _cacheattr_is_enabled + * + * (Internal macro.) + * Branches to \label if CACHEATTR in a2 indicates an enabled + * cache, using mask in a3. + * + * Parameters: + * label where to branch to if cache is enabled + * Entry: + * a2 contains CACHEATTR value used to determine whether + * caches are enabled + * a3 16-bit constant where each bit correspond to + * one of the 16 possible CA values (in a CACHEATTR mask); + * CA values that indicate the cache is enabled + * have their corresponding bit set in this mask + * (eg. use XCHAL_xCA_ENAMASK , above) + * Exit: + * a2,a4,a5 clobbered + * SAR clobbered + */ + .macro _cacheattr_is_enabled label + movi a4, 8 // loop 8 times +.Lcaife\@: + extui a5, a2, 0, 4 // get CA nibble + ssr a5 // index into mask according to CA... + srl a5, a3 // ...and get CA's mask bit in a5 bit 0 + bbsi.l a5, 0, \label // if CA indicates cache enabled, jump to label + srli a2, a2, 4 // next nibble + addi a4, a4, -1 + bnez a4, .Lcaife\@ // loop for each nibble + .endm + +#else /* XCHAL_HAVE_CACHEATTR || XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR */ + .macro _cacheattr_is_enabled label + j \label // macro not applicable, assume caches always enabled + .endm +#endif /* XCHAL_HAVE_CACHEATTR || XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR */ + + + +/* + * icacheattr_is_enabled + * + * Branches to \label if I-cache is enabled. + * + * Parameters: + * label where to branch to if icache is enabled + * Entry: + * (none) + * Exit: + * a2-a5, SAR clobbered (temporaries) + */ + .macro icacheattr_is_enabled label +#if XCHAL_HAVE_CACHEATTR || XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + icacheattr_get + movi a3, XCHAL_FCA_ENAMASK +#endif + _cacheattr_is_enabled \label + .endm + +/* + * dcacheattr_is_enabled + * + * Branches to \label if D-cache is enabled. + * + * Parameters: + * label where to branch to if dcache is enabled + * Entry: + * (none) + * Exit: + * a2-a5, SAR clobbered (temporaries) + */ + .macro dcacheattr_is_enabled label +#if XCHAL_HAVE_CACHEATTR || XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + dcacheattr_get + movi a3, XCHAL_LSCA_ENAMASK +#endif + _cacheattr_is_enabled \label + .endm + +/* + * cacheattr_is_enabled + * + * Branches to \label if either I-cache or D-cache is enabled. + * + * Parameters: + * label where to branch to if a cache is enabled + * Entry: + * (none) + * Exit: + * a2-a5, SAR clobbered (temporaries) + */ + .macro cacheattr_is_enabled label +#if XCHAL_HAVE_CACHEATTR + rsr a2, CACHEATTR + movi a3, XCHAL_ALLCA_ENAMASK +#elif XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + icacheattr_get + movi a3, XCHAL_FCA_ENAMASK + _cacheattr_is_enabled \label + dcacheattr_get + movi a3, XCHAL_LSCA_ENAMASK +#endif + _cacheattr_is_enabled \label + .endm + + + +/* + * The ISA does not have a defined way to change the + * instruction cache attributes of the running code, + * ie. of the memory area that encloses the current PC. + * However, each micro-architecture (or class of + * configurations within a micro-architecture) + * provides a way to deal with this issue. + * + * Here are a few macros used to implement the relevant + * approach taken. + */ + +#if XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + // We have a config that "mimics" CACHEATTR using a simplified + // "MMU" composed of a single statically-mapped way. + +/* + * icacheattr_set + * + * Entry: + * a2 cacheattr value to set + * Exit: + * a2 unchanged + * a3-a8 clobbered (temporaries) + */ + .macro icacheattr_set + + movi a5, 0xE0000000 // mask of upper 3 bits + movi a6, 3f // PC where ITLB is set + movi a3, 0 // start at region 0 (0 .. 7) + and a6, a6, a5 // upper 3 bits of local PC area + mov a7, a2 // copy a2 so it doesn't get clobbered + j 3f + +# if XCHAL_HAVE_XLT_CACHEATTR + // Can do translations, use generic method: +1: sub a6, a3, a5 // address of some other segment + ritlb1 a8, a6 // save its PPN+CA + dsync // interlock?? + witlb a4, a6 // make it translate to this code area + movi a6, 5f // where to jump into it + isync + sub a6, a6, a5 // adjust jump address within that other segment + jx a6 + + // Note that in the following code snippet, which runs at a different virtual + // address than it is assembled for, we avoid using literals (eg. via movi/l32r) + // just in case literals end up in a different 512 MB segment, and we avoid + // instructions that rely on the current PC being what is expected. + // + .align 4 + _j 6f // this is at label '5' minus 4 bytes + .align 4 +5: witlb a4, a3 // we're in other segment, now can write previous segment's CA + isync + add a6, a6, a5 // back to previous segment + addi a6, a6, -4 // next jump label + jx a6 + +6: sub a6, a3, a5 // address of some other segment + witlb a8, a6 // restore PPN+CA of other segment + mov a6, a3 // restore a6 + isync +# else /* XCHAL_HAVE_XLT_CACHEATTR */ + // Use micro-architecture specific method. + // The following 4-instruction sequence is aligned such that + // it all fits within a single I-cache line. Sixteen byte + // alignment is sufficient for this (using XCHAL_ICACHE_LINESIZE + // actually causes problems because that can be greater than + // the alignment of the reset vector, where this macro is often + // invoked, which would cause the linker to align the reset + // vector code away from the reset vector!!). + .align 16 /*XCHAL_ICACHE_LINESIZE*/ +1: _witlb a4, a3 // write wired PTE (CA, no PPN) of 512MB segment to ITLB + _isync + nop + nop +# endif /* XCHAL_HAVE_XLT_CACHEATTR */ + beq a3, a5, 4f // done? + + // Note that in the WITLB loop, we don't do any load/stores + // (may not be an issue here, but it is important in the DTLB case). +2: srli a7, a7, 4 // next CA + sub a3, a3, a5 // next segment (add 0x20000000) +3: +# if XCHAL_HAVE_XLT_CACHEATTR /* if have translation, preserve it */ + ritlb1 a8, a3 // get current PPN+CA of segment + dsync // interlock??? + extui a4, a7, 0, 4 // extract CA to set + srli a8, a8, 4 // clear CA but keep PPN ... + slli a8, a8, 4 // ... + add a4, a4, a8 // combine new CA with PPN to preserve +# else + extui a4, a7, 0, 4 // extract CA +# endif + beq a3, a6, 1b // current PC's region? if so, do it in a safe way + witlb a4, a3 // write wired PTE (CA [+PPN]) of 512MB segment to ITLB + bne a3, a5, 2b + isync // make sure all ifetch changes take effect +4: + .endm // icacheattr_set + + +/* + * dcacheattr_set + * + * Entry: + * a2 cacheattr value to set + * Exit: + * a2 unchanged + * a3-a8 clobbered (temporaries) + */ + + .macro dcacheattr_set + + movi a5, 0xE0000000 // mask of upper 3 bits + movi a3, 0 // start at region 0 (0 .. 7) + mov a7, a2 // copy a2 so it doesn't get clobbered + j 3f + // Note that in the WDTLB loop, we don't do any load/stores + // (including implicit l32r via movi) because it isn't safe. +2: srli a7, a7, 4 // next CA + sub a3, a3, a5 // next segment (add 0x20000000) +3: +# if XCHAL_HAVE_XLT_CACHEATTR /* if have translation, preserve it */ + rdtlb1 a8, a3 // get current PPN+CA of segment + dsync // interlock??? + extui a4, a7, 0, 4 // extract CA to set + srli a8, a8, 4 // clear CA but keep PPN ... + slli a8, a8, 4 // ... + add a4, a4, a8 // combine new CA with PPN to preserve +# else + extui a4, a7, 0, 4 // extract CA to set +# endif + wdtlb a4, a3 // write wired PTE (CA [+PPN]) of 512MB segment to DTLB + bne a3, a5, 2b + dsync // make sure all data path changes take effect + .endm // dcacheattr_set + +#endif /* XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR */ + + + +/* + * cacheattr_set + * + * Macro that sets the current CACHEATTR safely + * (both i and d) according to the current contents of a2. + * It works even when changing the cache attributes of + * the currently running code. + * + * Entry: + * a2 cacheattr value to set + * Exit: + * a2 unchanged + * a3-a8 clobbered (temporaries) + */ + .macro cacheattr_set + +#if XCHAL_HAVE_CACHEATTR +# if XCHAL_ICACHE_LINESIZE < 4 + // No i-cache, so can always safely write to CACHEATTR: + wsr a2, CACHEATTR +# else + // The Athens micro-architecture, when using the old + // exception architecture option (ie. with the CACHEATTR register) + // allows changing the cache attributes of the running code + // using the following exact sequence aligned to be within + // an instruction cache line. (NOTE: using XCHAL_ICACHE_LINESIZE + // alignment actually causes problems because that can be greater + // than the alignment of the reset vector, where this macro is often + // invoked, which would cause the linker to align the reset + // vector code away from the reset vector!!). + j 1f + .align 16 /*XCHAL_ICACHE_LINESIZE*/ // align to within an I-cache line +1: _wsr a2, CACHEATTR + _isync + nop + nop +# endif +#elif XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR + // DTLB and ITLB are independent, but to keep semantics + // of this macro we simply write to both. + icacheattr_set + dcacheattr_set +#else + // This macro isn't applicable to arbitrary MMU configurations. + // Do nothing in this case. +#endif + .endm + + +#endif /*XTENSA_CACHEATTRASM_H*/ + diff --git a/include/asm-xtensa/xtensa/config-linux_be/core.h b/include/asm-xtensa/xtensa/config-linux_be/core.h new file mode 100644 index 000000000000..d54fe5eb1064 --- /dev/null +++ b/include/asm-xtensa/xtensa/config-linux_be/core.h @@ -0,0 +1,1270 @@ +/* + * xtensa/config/core.h -- HAL definitions that are dependent on CORE configuration + * + * This header file is sometimes referred to as the "compile-time HAL" or CHAL. + * It was generated for a specific Xtensa processor configuration. + * + * Source for configuration-independent binaries (which link in a + * configuration-specific HAL library) must NEVER include this file. + * It is perfectly normal, however, for the HAL source itself to include this file. + */ + +/* + * Copyright (c) 2003 Tensilica, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2.1 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + + +#ifndef XTENSA_CONFIG_CORE_H +#define XTENSA_CONFIG_CORE_H + +#include + + +/*---------------------------------------------------------------------- + GENERAL + ----------------------------------------------------------------------*/ + +/* + * Separators for macros that expand into arrays. + * These can be predefined by files that #include this one, + * when different separators are required. + */ +/* Element separator for macros that expand into 1-dimensional arrays: */ +#ifndef XCHAL_SEP +#define XCHAL_SEP , +#endif +/* Array separator for macros that expand into 2-dimensional arrays: */ +#ifndef XCHAL_SEP2 +#define XCHAL_SEP2 },{ +#endif + + +/*---------------------------------------------------------------------- + ENDIANNESS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_BE 1 +#define XCHAL_HAVE_LE 0 +#define XCHAL_MEMORY_ORDER XTHAL_BIGENDIAN + + +/*---------------------------------------------------------------------- + REGISTER WINDOWS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_WINDOWED 1 /* 1 if windowed registers option configured, 0 otherwise */ +#define XCHAL_NUM_AREGS 64 /* number of physical address regs */ +#define XCHAL_NUM_AREGS_LOG2 6 /* log2(XCHAL_NUM_AREGS) */ + + +/*---------------------------------------------------------------------- + ADDRESS ALIGNMENT + ----------------------------------------------------------------------*/ + +/* These apply to a selected set of core load and store instructions only (see ISA): */ +#define XCHAL_UNALIGNED_LOAD_EXCEPTION 1 /* 1 if unaligned loads cause an exception, 0 otherwise */ +#define XCHAL_UNALIGNED_STORE_EXCEPTION 1 /* 1 if unaligned stores cause an exception, 0 otherwise */ + + +/*---------------------------------------------------------------------- + INTERRUPTS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_INTERRUPTS 1 /* 1 if interrupt option configured, 0 otherwise */ +#define XCHAL_HAVE_HIGHPRI_INTERRUPTS 1 /* 1 if high-priority interrupt option configured, 0 otherwise */ +#define XCHAL_HAVE_HIGHLEVEL_INTERRUPTS XCHAL_HAVE_HIGHPRI_INTERRUPTS +#define XCHAL_HAVE_NMI 0 /* 1 if NMI option configured, 0 otherwise */ +#define XCHAL_NUM_INTERRUPTS 17 /* number of interrupts */ +#define XCHAL_NUM_INTERRUPTS_LOG2 5 /* number of bits to hold an interrupt number: roundup(log2(number of interrupts)) */ +#define XCHAL_NUM_EXTINTERRUPTS 10 /* number of external interrupts */ +#define XCHAL_NUM_INTLEVELS 4 /* number of interrupt levels (not including level zero!) */ +#define XCHAL_NUM_LOWPRI_LEVELS 1 /* number of low-priority interrupt levels (always 1) */ +#define XCHAL_FIRST_HIGHPRI_LEVEL (XCHAL_NUM_LOWPRI_LEVELS+1) /* level of first high-priority interrupt (always 2) */ +#define XCHAL_EXCM_LEVEL 1 /* level of interrupts masked by PS.EXCM (XEA2 only; always 1 in T10xx); + for XEA1, where there is no PS.EXCM, this is always 1; + interrupts at levels FIRST_HIGHPRI <= n <= EXCM_LEVEL, if any, + are termed "medium priority" interrupts (post T10xx only) */ +/* Note: 1 <= LOWPRI_LEVELS <= EXCM_LEVEL < DEBUGLEVEL <= NUM_INTLEVELS < NMILEVEL <= 15 */ + +/* Masks of interrupts at each interrupt level: */ +#define XCHAL_INTLEVEL0_MASK 0x00000000 +#define XCHAL_INTLEVEL1_MASK 0x000064F9 +#define XCHAL_INTLEVEL2_MASK 0x00008902 +#define XCHAL_INTLEVEL3_MASK 0x00011204 +#define XCHAL_INTLEVEL4_MASK 0x00000000 +#define XCHAL_INTLEVEL5_MASK 0x00000000 +#define XCHAL_INTLEVEL6_MASK 0x00000000 +#define XCHAL_INTLEVEL7_MASK 0x00000000 +#define XCHAL_INTLEVEL8_MASK 0x00000000 +#define XCHAL_INTLEVEL9_MASK 0x00000000 +#define XCHAL_INTLEVEL10_MASK 0x00000000 +#define XCHAL_INTLEVEL11_MASK 0x00000000 +#define XCHAL_INTLEVEL12_MASK 0x00000000 +#define XCHAL_INTLEVEL13_MASK 0x00000000 +#define XCHAL_INTLEVEL14_MASK 0x00000000 +#define XCHAL_INTLEVEL15_MASK 0x00000000 +/* As an array of entries (eg. for C constant arrays): */ +#define XCHAL_INTLEVEL_MASKS 0x00000000 XCHAL_SEP \ + 0x000064F9 XCHAL_SEP \ + 0x00008902 XCHAL_SEP \ + 0x00011204 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 XCHAL_SEP \ + 0x00000000 + +/* Masks of interrupts at each range 1..n of interrupt levels: */ +#define XCHAL_INTLEVEL0_ANDBELOW_MASK 0x00000000 +#define XCHAL_INTLEVEL1_ANDBELOW_MASK 0x000064F9 +#define XCHAL_INTLEVEL2_ANDBELOW_MASK 0x0000EDFB +#define XCHAL_INTLEVEL3_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL4_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL5_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL6_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL7_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL8_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL9_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL10_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL11_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL12_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL13_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL14_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_INTLEVEL15_ANDBELOW_MASK 0x0001FFFF +#define XCHAL_LOWPRI_MASK XCHAL_INTLEVEL1_ANDBELOW_MASK /* mask of all low-priority interrupts */ +#define XCHAL_EXCM_MASK XCHAL_INTLEVEL1_ANDBELOW_MASK /* mask of all interrupts masked by PS.EXCM (or CEXCM) */ +/* As an array of entries (eg. for C constant arrays): */ +#define XCHAL_INTLEVEL_ANDBELOW_MASKS 0x00000000 XCHAL_SEP \ + 0x000064F9 XCHAL_SEP \ + 0x0000EDFB XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF XCHAL_SEP \ + 0x0001FFFF + +/* Interrupt numbers for each interrupt level at which only one interrupt was configured: */ +/*#define XCHAL_INTLEVEL1_NUM ...more than one interrupt at this level...*/ +/*#define XCHAL_INTLEVEL2_NUM ...more than one interrupt at this level...*/ +/*#define XCHAL_INTLEVEL3_NUM ...more than one interrupt at this level...*/ + +/* Level of each interrupt: */ +#define XCHAL_INT0_LEVEL 1 +#define XCHAL_INT1_LEVEL 2 +#define XCHAL_INT2_LEVEL 3 +#define XCHAL_INT3_LEVEL 1 +#define XCHAL_INT4_LEVEL 1 +#define XCHAL_INT5_LEVEL 1 +#define XCHAL_INT6_LEVEL 1 +#define XCHAL_INT7_LEVEL 1 +#define XCHAL_INT8_LEVEL 2 +#define XCHAL_INT9_LEVEL 3 +#define XCHAL_INT10_LEVEL 1 +#define XCHAL_INT11_LEVEL 2 +#define XCHAL_INT12_LEVEL 3 +#define XCHAL_INT13_LEVEL 1 +#define XCHAL_INT14_LEVEL 1 +#define XCHAL_INT15_LEVEL 2 +#define XCHAL_INT16_LEVEL 3 +#define XCHAL_INT17_LEVEL 0 +#define XCHAL_INT18_LEVEL 0 +#define XCHAL_INT19_LEVEL 0 +#define XCHAL_INT20_LEVEL 0 +#define XCHAL_INT21_LEVEL 0 +#define XCHAL_INT22_LEVEL 0 +#define XCHAL_INT23_LEVEL 0 +#define XCHAL_INT24_LEVEL 0 +#define XCHAL_INT25_LEVEL 0 +#define XCHAL_INT26_LEVEL 0 +#define XCHAL_INT27_LEVEL 0 +#define XCHAL_INT28_LEVEL 0 +#define XCHAL_INT29_LEVEL 0 +#define XCHAL_INT30_LEVEL 0 +#define XCHAL_INT31_LEVEL 0 +/* As an array of entries (eg. for C constant arrays): */ +#define XCHAL_INT_LEVELS 1 XCHAL_SEP \ + 2 XCHAL_SEP \ + 3 XCHAL_SEP \ + 1 XCHAL_SEP \ + 1 XCHAL_SEP \ + 1 XCHAL_SEP \ + 1 XCHAL_SEP \ + 1 XCHAL_SEP \ + 2 XCHAL_SEP \ + 3 XCHAL_SEP \ + 1 XCHAL_SEP \ + 2 XCHAL_SEP \ + 3 XCHAL_SEP \ + 1 XCHAL_SEP \ + 1 XCHAL_SEP \ + 2 XCHAL_SEP \ + 3 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 XCHAL_SEP \ + 0 + +/* Type of each interrupt: */ +#define XCHAL_INT0_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT1_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT2_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT3_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT4_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT5_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT6_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT7_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT8_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT9_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT10_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT11_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT12_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT13_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT14_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT15_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT16_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT17_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT18_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT19_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT20_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT21_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT22_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT23_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT24_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT25_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT26_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT27_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT28_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT29_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT30_TYPE XTHAL_INTTYPE_UNCONFIGURED +#define XCHAL_INT31_TYPE XTHAL_INTTYPE_UNCONFIGURED +/* As an array of entries (eg. for C constant arrays): */ +#define XCHAL_INT_TYPES XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_LEVEL XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_EDGE XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_EDGE XCHAL_SEP \ + XTHAL_INTTYPE_EXTERN_EDGE XCHAL_SEP \ + XTHAL_INTTYPE_TIMER XCHAL_SEP \ + XTHAL_INTTYPE_TIMER XCHAL_SEP \ + XTHAL_INTTYPE_TIMER XCHAL_SEP \ + XTHAL_INTTYPE_SOFTWARE XCHAL_SEP \ + XTHAL_INTTYPE_SOFTWARE XCHAL_SEP \ + XTHAL_INTTYPE_SOFTWARE XCHAL_SEP \ + XTHAL_INTTYPE_SOFTWARE XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED XCHAL_SEP \ + XTHAL_INTTYPE_UNCONFIGURED + +/* Masks of interrupts for each type of interrupt: */ +#define XCHAL_INTTYPE_MASK_UNCONFIGURED 0xFFFE0000 +#define XCHAL_INTTYPE_MASK_SOFTWARE 0x0001E000 +#define XCHAL_INTTYPE_MASK_EXTERN_EDGE 0x00000380 +#define XCHAL_INTTYPE_MASK_EXTERN_LEVEL 0x0000007F +#define XCHAL_INTTYPE_MASK_TIMER 0x00001C00 +#define XCHAL_INTTYPE_MASK_NMI 0x00000000 +/* As an array of entries (eg. for C constant arrays): */ +#define XCHAL_INTTYPE_MASKS 0xFFFE0000 XCHAL_SEP \ + 0x0001E000 XCHAL_SEP \ + 0x00000380 XCHAL_SEP \ + 0x0000007F XCHAL_SEP \ + 0x00001C00 XCHAL_SEP \ + 0x00000000 + +/* Interrupts assigned to each timer (CCOMPARE0 to CCOMPARE3), -1 if unassigned */ +#define XCHAL_TIMER0_INTERRUPT 10 +#define XCHAL_TIMER1_INTERRUPT 11 +#define XCHAL_TIMER2_INTERRUPT 12 +#define XCHAL_TIMER3_INTERRUPT XTHAL_TIMER_UNCONFIGURED +/* As an array of entries (eg. for C constant arrays): */ +#define XCHAL_TIMER_INTERRUPTS 10 XCHAL_SEP \ + 11 XCHAL_SEP \ + 12 XCHAL_SEP \ + XTHAL_TIMER_UNCONFIGURED + +/* Indexing macros: */ +#define _XCHAL_INTLEVEL_MASK(n) XCHAL_INTLEVEL ## n ## _MASK +#define XCHAL_INTLEVEL_MASK(n) _XCHAL_INTLEVEL_MASK(n) /* n = 0 .. 15 */ +#define _XCHAL_INTLEVEL_ANDBELOWMASK(n) XCHAL_INTLEVEL ## n ## _ANDBELOW_MASK +#define XCHAL_INTLEVEL_ANDBELOW_MASK(n) _XCHAL_INTLEVEL_ANDBELOWMASK(n) /* n = 0 .. 15 */ +#define _XCHAL_INT_LEVEL(n) XCHAL_INT ## n ## _LEVEL +#define XCHAL_INT_LEVEL(n) _XCHAL_INT_LEVEL(n) /* n = 0 .. 31 */ +#define _XCHAL_INT_TYPE(n) XCHAL_INT ## n ## _TYPE +#define XCHAL_INT_TYPE(n) _XCHAL_INT_TYPE(n) /* n = 0 .. 31 */ +#define _XCHAL_TIMER_INTERRUPT(n) XCHAL_TIMER ## n ## _INTERRUPT +#define XCHAL_TIMER_INTERRUPT(n) _XCHAL_TIMER_INTERRUPT(n) /* n = 0 .. 3 */ + + + +/* + * External interrupt vectors/levels. + * These macros describe how Xtensa processor interrupt numbers + * (as numbered internally, eg. in INTERRUPT and INTENABLE registers) + * map to external BInterrupt pins, for those interrupts + * configured as external (level-triggered, edge-triggered, or NMI). + * See the Xtensa processor databook for more details. + */ + +/* Core interrupt numbers mapped to each EXTERNAL interrupt number: */ +#define XCHAL_EXTINT0_NUM 0 /* (intlevel 1) */ +#define XCHAL_EXTINT1_NUM 1 /* (intlevel 2) */ +#define XCHAL_EXTINT2_NUM 2 /* (intlevel 3) */ +#define XCHAL_EXTINT3_NUM 3 /* (intlevel 1) */ +#define XCHAL_EXTINT4_NUM 4 /* (intlevel 1) */ +#define XCHAL_EXTINT5_NUM 5 /* (intlevel 1) */ +#define XCHAL_EXTINT6_NUM 6 /* (intlevel 1) */ +#define XCHAL_EXTINT7_NUM 7 /* (intlevel 1) */ +#define XCHAL_EXTINT8_NUM 8 /* (intlevel 2) */ +#define XCHAL_EXTINT9_NUM 9 /* (intlevel 3) */ + +/* Corresponding interrupt masks: */ +#define XCHAL_EXTINT0_MASK 0x00000001 +#define XCHAL_EXTINT1_MASK 0x00000002 +#define XCHAL_EXTINT2_MASK 0x00000004 +#define XCHAL_EXTINT3_MASK 0x00000008 +#define XCHAL_EXTINT4_MASK 0x00000010 +#define XCHAL_EXTINT5_MASK 0x00000020 +#define XCHAL_EXTINT6_MASK 0x00000040 +#define XCHAL_EXTINT7_MASK 0x00000080 +#define XCHAL_EXTINT8_MASK 0x00000100 +#define XCHAL_EXTINT9_MASK 0x00000200 + +/* Core config interrupt levels mapped to each external interrupt: */ +#define XCHAL_EXTINT0_LEVEL 1 /* (int number 0) */ +#define XCHAL_EXTINT1_LEVEL 2 /* (int number 1) */ +#define XCHAL_EXTINT2_LEVEL 3 /* (int number 2) */ +#define XCHAL_EXTINT3_LEVEL 1 /* (int number 3) */ +#define XCHAL_EXTINT4_LEVEL 1 /* (int number 4) */ +#define XCHAL_EXTINT5_LEVEL 1 /* (int number 5) */ +#define XCHAL_EXTINT6_LEVEL 1 /* (int number 6) */ +#define XCHAL_EXTINT7_LEVEL 1 /* (int number 7) */ +#define XCHAL_EXTINT8_LEVEL 2 /* (int number 8) */ +#define XCHAL_EXTINT9_LEVEL 3 /* (int number 9) */ + + +/*---------------------------------------------------------------------- + EXCEPTIONS and VECTORS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_EXCEPTIONS 1 /* 1 if exception option configured, 0 otherwise */ + +#define XCHAL_XEA_VERSION 2 /* Xtensa Exception Architecture number: 1 for XEA1 (old), 2 for XEA2 (new) */ +#define XCHAL_HAVE_XEA1 0 /* 1 if XEA1, 0 otherwise */ +#define XCHAL_HAVE_XEA2 1 /* 1 if XEA2, 0 otherwise */ +/* For backward compatibility ONLY -- DO NOT USE (will be removed in future release): */ +#define XCHAL_HAVE_OLD_EXC_ARCH XCHAL_HAVE_XEA1 /* (DEPRECATED) 1 if old exception architecture (XEA1), 0 otherwise (eg. XEA2) */ +#define XCHAL_HAVE_EXCM XCHAL_HAVE_XEA2 /* (DEPRECATED) 1 if PS.EXCM bit exists (currently equals XCHAL_HAVE_TLBS) */ + +#define XCHAL_RESET_VECTOR_VADDR 0xFE000020 +#define XCHAL_RESET_VECTOR_PADDR 0xFE000020 +#define XCHAL_USER_VECTOR_VADDR 0xD0000220 +#define XCHAL_PROGRAMEXC_VECTOR_VADDR XCHAL_USER_VECTOR_VADDR /* for backward compatibility */ +#define XCHAL_USEREXC_VECTOR_VADDR XCHAL_USER_VECTOR_VADDR /* for backward compatibility */ +#define XCHAL_USER_VECTOR_PADDR 0x00000220 +#define XCHAL_PROGRAMEXC_VECTOR_PADDR XCHAL_USER_VECTOR_PADDR /* for backward compatibility */ +#define XCHAL_USEREXC_VECTOR_PADDR XCHAL_USER_VECTOR_PADDR /* for backward compatibility */ +#define XCHAL_KERNEL_VECTOR_VADDR 0xD0000200 +#define XCHAL_STACKEDEXC_VECTOR_VADDR XCHAL_KERNEL_VECTOR_VADDR /* for backward compatibility */ +#define XCHAL_KERNELEXC_VECTOR_VADDR XCHAL_KERNEL_VECTOR_VADDR /* for backward compatibility */ +#define XCHAL_KERNEL_VECTOR_PADDR 0x00000200 +#define XCHAL_STACKEDEXC_VECTOR_PADDR XCHAL_KERNEL_VECTOR_PADDR /* for backward compatibility */ +#define XCHAL_KERNELEXC_VECTOR_PADDR XCHAL_KERNEL_VECTOR_PADDR /* for backward compatibility */ +#define XCHAL_DOUBLEEXC_VECTOR_VADDR 0xD0000290 +#define XCHAL_DOUBLEEXC_VECTOR_PADDR 0x00000290 +#define XCHAL_WINDOW_VECTORS_VADDR 0xD0000000 +#define XCHAL_WINDOW_VECTORS_PADDR 0x00000000 +#define XCHAL_INTLEVEL2_VECTOR_VADDR 0xD0000240 +#define XCHAL_INTLEVEL2_VECTOR_PADDR 0x00000240 +#define XCHAL_INTLEVEL3_VECTOR_VADDR 0xD0000250 +#define XCHAL_INTLEVEL3_VECTOR_PADDR 0x00000250 +#define XCHAL_INTLEVEL4_VECTOR_VADDR 0xFE000520 +#define XCHAL_INTLEVEL4_VECTOR_PADDR 0xFE000520 +#define XCHAL_DEBUG_VECTOR_VADDR XCHAL_INTLEVEL4_VECTOR_VADDR +#define XCHAL_DEBUG_VECTOR_PADDR XCHAL_INTLEVEL4_VECTOR_PADDR + +/* Indexing macros: */ +#define _XCHAL_INTLEVEL_VECTOR_VADDR(n) XCHAL_INTLEVEL ## n ## _VECTOR_VADDR +#define XCHAL_INTLEVEL_VECTOR_VADDR(n) _XCHAL_INTLEVEL_VECTOR_VADDR(n) /* n = 0 .. 15 */ + +/* + * General Exception Causes + * (values of EXCCAUSE special register set by general exceptions, + * which vector to the user, kernel, or double-exception vectors): + */ +#define XCHAL_EXCCAUSE_ILLEGAL_INSTRUCTION 0 /* Illegal Instruction (IllegalInstruction) */ +#define XCHAL_EXCCAUSE_SYSTEM_CALL 1 /* System Call (SystemCall) */ +#define XCHAL_EXCCAUSE_INSTRUCTION_FETCH_ERROR 2 /* Instruction Fetch Error (InstructionFetchError) */ +#define XCHAL_EXCCAUSE_LOAD_STORE_ERROR 3 /* Load Store Error (LoadStoreError) */ +#define XCHAL_EXCCAUSE_LEVEL1_INTERRUPT 4 /* Level 1 Interrupt (Level1Interrupt) */ +#define XCHAL_EXCCAUSE_ALLOCA 5 /* Stack Extension Assist (Alloca) */ +#define XCHAL_EXCCAUSE_INTEGER_DIVIDE_BY_ZERO 6 /* Integer Divide by Zero (IntegerDivideByZero) */ +#define XCHAL_EXCCAUSE_SPECULATION 7 /* Speculation (Speculation) */ +#define XCHAL_EXCCAUSE_PRIVILEGED 8 /* Privileged Instruction (Privileged) */ +#define XCHAL_EXCCAUSE_UNALIGNED 9 /* Unaligned Load Store (Unaligned) */ +#define XCHAL_EXCCAUSE_ITLB_MISS 16 /* ITlb Miss Exception (ITlbMiss) */ +#define XCHAL_EXCCAUSE_ITLB_MULTIHIT 17 /* ITlb Mutltihit Exception (ITlbMultihit) */ +#define XCHAL_EXCCAUSE_ITLB_PRIVILEGE 18 /* ITlb Privilege Exception (ITlbPrivilege) */ +#define XCHAL_EXCCAUSE_ITLB_SIZE_RESTRICTION 19 /* ITlb Size Restriction Exception (ITlbSizeRestriction) */ +#define XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE 20 /* Fetch Cache Attribute Exception (FetchCacheAttribute) */ +#define XCHAL_EXCCAUSE_DTLB_MISS 24 /* DTlb Miss Exception (DTlbMiss) */ +#define XCHAL_EXCCAUSE_DTLB_MULTIHIT 25 /* DTlb Multihit Exception (DTlbMultihit) */ +#define XCHAL_EXCCAUSE_DTLB_PRIVILEGE 26 /* DTlb Privilege Exception (DTlbPrivilege) */ +#define XCHAL_EXCCAUSE_DTLB_SIZE_RESTRICTION 27 /* DTlb Size Restriction Exception (DTlbSizeRestriction) */ +#define XCHAL_EXCCAUSE_LOAD_CACHE_ATTRIBUTE 28 /* Load Cache Attribute Exception (LoadCacheAttribute) */ +#define XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE 29 /* Store Cache Attribute Exception (StoreCacheAttribute) */ +#define XCHAL_EXCCAUSE_FLOATING_POINT 40 /* Floating Point Exception (FloatingPoint) */ + + + +/*---------------------------------------------------------------------- + TIMERS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_CCOUNT 1 /* 1 if have CCOUNT, 0 otherwise */ +/*#define XCHAL_HAVE_TIMERS XCHAL_HAVE_CCOUNT*/ +#define XCHAL_NUM_TIMERS 3 /* number of CCOMPAREn regs */ + + + +/*---------------------------------------------------------------------- + DEBUG + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_DEBUG 1 /* 1 if debug option configured, 0 otherwise */ +#define XCHAL_HAVE_OCD 1 /* 1 if OnChipDebug option configured, 0 otherwise */ +#define XCHAL_NUM_IBREAK 2 /* number of IBREAKn regs */ +#define XCHAL_NUM_DBREAK 2 /* number of DBREAKn regs */ +#define XCHAL_DEBUGLEVEL 4 /* debug interrupt level */ +/*DebugExternalInterrupt 0 0|1*/ +/*DebugUseDIRArray 0 0|1*/ + + + + +/*---------------------------------------------------------------------- + COPROCESSORS and EXTRA STATE + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_CP 0 /* 1 if coprocessor option configured (CPENABLE present) */ +#define XCHAL_CP_MAXCFG 0 /* max allowed cp id plus one (per cfg) */ + +#include + + + + +/*---------------------------------------------------------------------- + INTERNAL I/D RAM/ROMs and XLMI + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_INSTROM 0 /* number of core instruction ROMs configured */ +#define XCHAL_NUM_INSTRAM 0 /* number of core instruction RAMs configured */ +#define XCHAL_NUM_DATAROM 0 /* number of core data ROMs configured */ +#define XCHAL_NUM_DATARAM 0 /* number of core data RAMs configured */ +#define XCHAL_NUM_XLMI 0 /* number of core XLMI ports configured */ +#define XCHAL_NUM_IROM XCHAL_NUM_INSTROM /* (DEPRECATED) */ +#define XCHAL_NUM_IRAM XCHAL_NUM_INSTRAM /* (DEPRECATED) */ +#define XCHAL_NUM_DROM XCHAL_NUM_DATAROM /* (DEPRECATED) */ +#define XCHAL_NUM_DRAM XCHAL_NUM_DATARAM /* (DEPRECATED) */ + + + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +/* Size of the cache lines in log2(bytes): */ +#define XCHAL_ICACHE_LINEWIDTH 4 +#define XCHAL_DCACHE_LINEWIDTH 4 +/* Size of the cache lines in bytes: */ +#define XCHAL_ICACHE_LINESIZE 16 +#define XCHAL_DCACHE_LINESIZE 16 +/* Max for both I-cache and D-cache (used for general alignment): */ +#define XCHAL_CACHE_LINEWIDTH_MAX 4 +#define XCHAL_CACHE_LINESIZE_MAX 16 + +/* Number of cache sets in log2(lines per way): */ +#define XCHAL_ICACHE_SETWIDTH 8 +#define XCHAL_DCACHE_SETWIDTH 8 +/* Max for both I-cache and D-cache (used for general cache-coherency page alignment): */ +#define XCHAL_CACHE_SETWIDTH_MAX 8 +#define XCHAL_CACHE_SETSIZE_MAX 256 + +/* Cache set associativity (number of ways): */ +#define XCHAL_ICACHE_WAYS 2 +#define XCHAL_DCACHE_WAYS 2 + +/* Size of the caches in bytes (ways * 2^(linewidth + setwidth)): */ +#define XCHAL_ICACHE_SIZE 8192 +#define XCHAL_DCACHE_SIZE 8192 + +/* Cache features: */ +#define XCHAL_DCACHE_IS_WRITEBACK 0 +/* Whether cache locking feature is available: */ +#define XCHAL_ICACHE_LINE_LOCKABLE 0 +#define XCHAL_DCACHE_LINE_LOCKABLE 0 + +/* Number of (encoded) cache attribute bits: */ +#define XCHAL_CA_BITS 4 /* number of bits needed to hold cache attribute encoding */ +/* (The number of access mode bits (decoded cache attribute bits) is defined by the architecture; see xtensa/hal.h?) */ + + +/* Cache Attribute encodings -- lists of access modes for each cache attribute: */ +#define XCHAL_FCA_LIST XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_BYPASS XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_CACHED XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_CACHED XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_CACHED XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_CACHED XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION XCHAL_SEP \ + XTHAL_FAM_EXCEPTION +#define XCHAL_LCA_LIST XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_BYPASSG XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_CACHED XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_CACHED XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_NACACHED XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_NACACHED XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_ISOLATE XCHAL_SEP \ + XTHAL_LAM_EXCEPTION XCHAL_SEP \ + XTHAL_LAM_CACHED +#define XCHAL_SCA_LIST XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_BYPASS XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_WRITETHRU XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_WRITETHRU XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_ISOLATE XCHAL_SEP \ + XTHAL_SAM_EXCEPTION XCHAL_SEP \ + XTHAL_SAM_WRITETHRU + +/* Test: + read/only: 0 + 1 + 2 + 4 + 5 + 6 + 8 + 9 + 10 + 12 + 14 + read/only: 0 + 1 + 2 + 4 + 5 + 6 + 8 + 9 + 10 + 12 + 14 + all: 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + fault: 0 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + r/w/x cached: + r/w/x dcached: + I-bypass: 1 + 3 + + load guard bit set: 1 + 3 + load guard bit clr: 0 + 2 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + hit-cache r/w/x: 7 + 11 + + fams: 5 + fams: 0 / 6 / 18 / 1 / 2 + fams: Bypass / Isolate / Cached / Exception / NACached + + MMU okay: yes +*/ + + +/*---------------------------------------------------------------------- + MMU + ----------------------------------------------------------------------*/ + +/* + * General notes on MMU parameters. + * + * Terminology: + * ASID = address-space ID (acts as an "extension" of virtual addresses) + * VPN = virtual page number + * PPN = physical page number + * CA = encoded cache attribute (access modes) + * TLB = translation look-aside buffer (term is stretched somewhat here) + * I = instruction (fetch accesses) + * D = data (load and store accesses) + * way = each TLB (ITLB and DTLB) consists of a number of "ways" + * that simultaneously match the virtual address of an access; + * a TLB successfully translates a virtual address if exactly + * one way matches the vaddr; if none match, it is a miss; + * if multiple match, one gets a "multihit" exception; + * each way can be independently configured in terms of number of + * entries, page sizes, which fields are writable or constant, etc. + * set = group of contiguous ways with exactly identical parameters + * ARF = auto-refill; hardware services a 1st-level miss by loading a PTE + * from the page table and storing it in one of the auto-refill ways; + * if this PTE load also misses, a miss exception is posted for s/w. + * min-wired = a "min-wired" way can be used to map a single (minimum-sized) + * page arbitrarily under program control; it has a single entry, + * is non-auto-refill (some other way(s) must be auto-refill), + * all its fields (VPN, PPN, ASID, CA) are all writable, and it + * supports the XCHAL_MMU_MIN_PTE_PAGE_SIZE page size (a current + * restriction is that this be the only page size it supports). + * + * TLB way entries are virtually indexed. + * TLB ways that support multiple page sizes: + * - must have all writable VPN and PPN fields; + * - can only use one page size at any given time (eg. setup at startup), + * selected by the respective ITLBCFG or DTLBCFG special register, + * whose bits n*4+3 .. n*4 index the list of page sizes for way n + * (XCHAL_xTLB_SETm_PAGESZ_LOG2_LIST for set m corresponding to way n); + * this list may be sparse for auto-refill ways because auto-refill + * ways have independent lists of supported page sizes sharing a + * common encoding with PTE entries; the encoding is the index into + * this list; unsupported sizes for a given way are zero in the list; + * selecting unsupported sizes results in undefined hardware behaviour; + * - is only possible for ways 0 thru 7 (due to ITLBCFG/DTLBCFG definition). + */ + +#define XCHAL_HAVE_CACHEATTR 0 /* 1 if CACHEATTR register present, 0 if TLBs present instead */ +#define XCHAL_HAVE_TLBS 1 /* 1 if TLBs present, 0 if CACHEATTR present instead */ +#define XCHAL_HAVE_MMU XCHAL_HAVE_TLBS /* (DEPRECATED; use XCHAL_HAVE_TLBS instead; will be removed in future release) */ +#define XCHAL_HAVE_SPANNING_WAY 0 /* 1 if single way maps entire virtual address space in I+D */ +#define XCHAL_HAVE_IDENTITY_MAP 0 /* 1 if virtual addr == physical addr always, 0 otherwise */ +#define XCHAL_HAVE_MIMIC_CACHEATTR 0 /* 1 if have MMU that mimics a CACHEATTR config (CaMMU) */ +#define XCHAL_HAVE_XLT_CACHEATTR 0 /* 1 if have MMU that mimics a CACHEATTR config, but with translation (CaXltMMU) */ + +#define XCHAL_MMU_ASID_BITS 8 /* number of bits in ASIDs (address space IDs) */ +#define XCHAL_MMU_ASID_INVALID 0 /* ASID value indicating invalid address space */ +#define XCHAL_MMU_ASID_KERNEL 1 /* ASID value indicating kernel (ring 0) address space */ +#define XCHAL_MMU_RINGS 4 /* number of rings supported (1..4) */ +#define XCHAL_MMU_RING_BITS 2 /* number of bits needed to hold ring number */ +#define XCHAL_MMU_SR_BITS 0 /* number of size-restriction bits supported */ +#define XCHAL_MMU_CA_BITS 4 /* number of bits needed to hold cache attribute encoding */ +#define XCHAL_MMU_MAX_PTE_PAGE_SIZE 12 /* max page size in a PTE structure (log2) */ +#define XCHAL_MMU_MIN_PTE_PAGE_SIZE 12 /* min page size in a PTE structure (log2) */ + + +/*** Instruction TLB: ***/ + +#define XCHAL_ITLB_WAY_BITS 3 /* number of bits holding the ways */ +#define XCHAL_ITLB_WAYS 7 /* number of ways (n-way set-associative TLB) */ +#define XCHAL_ITLB_ARF_WAYS 4 /* number of auto-refill ways */ +#define XCHAL_ITLB_SETS 4 /* number of sets (groups of ways with identical settings) */ + +/* Way set to which each way belongs: */ +#define XCHAL_ITLB_WAY0_SET 0 +#define XCHAL_ITLB_WAY1_SET 0 +#define XCHAL_ITLB_WAY2_SET 0 +#define XCHAL_ITLB_WAY3_SET 0 +#define XCHAL_ITLB_WAY4_SET 1 +#define XCHAL_ITLB_WAY5_SET 2 +#define XCHAL_ITLB_WAY6_SET 3 + +/* Ways sets that are used by hardware auto-refill (ARF): */ +#define XCHAL_ITLB_ARF_SETS 1 /* number of auto-refill sets */ +#define XCHAL_ITLB_ARF_SET0 0 /* index of n'th auto-refill set */ + +/* Way sets that are "min-wired" (see terminology comment above): */ +#define XCHAL_ITLB_MINWIRED_SETS 0 /* number of "min-wired" sets */ + + +/* ITLB way set 0 (group of ways 0 thru 3): */ +#define XCHAL_ITLB_SET0_WAY 0 /* index of first way in this way set */ +#define XCHAL_ITLB_SET0_WAYS 4 /* number of (contiguous) ways in this way set */ +#define XCHAL_ITLB_SET0_ENTRIES_LOG2 2 /* log2(number of entries in this way) */ +#define XCHAL_ITLB_SET0_ENTRIES 4 /* number of entries in this way (always a power of 2) */ +#define XCHAL_ITLB_SET0_ARF 1 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_ITLB_SET0_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_ITLB_SET0_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_ITLB_SET0_PAGESZ_LOG2_MIN 12 /* log2(minimum supported page size) */ +#define XCHAL_ITLB_SET0_PAGESZ_LOG2_MAX 12 /* log2(maximum supported page size) */ +#define XCHAL_ITLB_SET0_PAGESZ_LOG2_LIST 12 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_ITLB_SET0_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_VPN_CONSTMASK 0 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_PPN_CONSTMASK 0 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_ITLB_SET0_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET0_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET0_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET0_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ + +/* ITLB way set 1 (group of ways 4 thru 4): */ +#define XCHAL_ITLB_SET1_WAY 4 /* index of first way in this way set */ +#define XCHAL_ITLB_SET1_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_ITLB_SET1_ENTRIES_LOG2 2 /* log2(number of entries in this way) */ +#define XCHAL_ITLB_SET1_ENTRIES 4 /* number of entries in this way (always a power of 2) */ +#define XCHAL_ITLB_SET1_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_ITLB_SET1_PAGESIZES 4 /* number of supported page sizes in this way */ +#define XCHAL_ITLB_SET1_PAGESZ_BITS 2 /* number of bits to encode the page size */ +#define XCHAL_ITLB_SET1_PAGESZ_LOG2_MIN 20 /* log2(minimum supported page size) */ +#define XCHAL_ITLB_SET1_PAGESZ_LOG2_MAX 26 /* log2(maximum supported page size) */ +#define XCHAL_ITLB_SET1_PAGESZ_LOG2_LIST 20 XCHAL_SEP 22 XCHAL_SEP 24 XCHAL_SEP 26 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_ITLB_SET1_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_ITLB_SET1_VPN_CONSTMASK 0 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET1_PPN_CONSTMASK 0 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET1_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_ITLB_SET1_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET1_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET1_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET1_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ + +/* ITLB way set 2 (group of ways 5 thru 5): */ +#define XCHAL_ITLB_SET2_WAY 5 /* index of first way in this way set */ +#define XCHAL_ITLB_SET2_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_ITLB_SET2_ENTRIES_LOG2 1 /* log2(number of entries in this way) */ +#define XCHAL_ITLB_SET2_ENTRIES 2 /* number of entries in this way (always a power of 2) */ +#define XCHAL_ITLB_SET2_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_ITLB_SET2_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_ITLB_SET2_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_ITLB_SET2_PAGESZ_LOG2_MIN 27 /* log2(minimum supported page size) */ +#define XCHAL_ITLB_SET2_PAGESZ_LOG2_MAX 27 /* log2(maximum supported page size) */ +#define XCHAL_ITLB_SET2_PAGESZ_LOG2_LIST 27 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_ITLB_SET2_ASID_CONSTMASK 0xFF /* constant ASID bits; 0 if all writable */ +#define XCHAL_ITLB_SET2_VPN_CONSTMASK 0xF0000000 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET2_PPN_CONSTMASK 0xF8000000 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET2_CA_CONSTMASK 0x0000000F /* constant CA bits; 0 if all writable */ +#define XCHAL_ITLB_SET2_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET2_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET2_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET2_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ +/* Constant ASID values for each entry of ITLB way set 2 (because ASID_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET2_E0_ASID_CONST 0x01 +#define XCHAL_ITLB_SET2_E1_ASID_CONST 0x01 +/* Constant VPN values for each entry of ITLB way set 2 (because VPN_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET2_E0_VPN_CONST 0xD0000000 +#define XCHAL_ITLB_SET2_E1_VPN_CONST 0xD8000000 +/* Constant PPN values for each entry of ITLB way set 2 (because PPN_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET2_E0_PPN_CONST 0x00000000 +#define XCHAL_ITLB_SET2_E1_PPN_CONST 0x00000000 +/* Constant CA values for each entry of ITLB way set 2 (because CA_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET2_E0_CA_CONST 0x07 +#define XCHAL_ITLB_SET2_E1_CA_CONST 0x03 + +/* ITLB way set 3 (group of ways 6 thru 6): */ +#define XCHAL_ITLB_SET3_WAY 6 /* index of first way in this way set */ +#define XCHAL_ITLB_SET3_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_ITLB_SET3_ENTRIES_LOG2 1 /* log2(number of entries in this way) */ +#define XCHAL_ITLB_SET3_ENTRIES 2 /* number of entries in this way (always a power of 2) */ +#define XCHAL_ITLB_SET3_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_ITLB_SET3_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_ITLB_SET3_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_ITLB_SET3_PAGESZ_LOG2_MIN 28 /* log2(minimum supported page size) */ +#define XCHAL_ITLB_SET3_PAGESZ_LOG2_MAX 28 /* log2(maximum supported page size) */ +#define XCHAL_ITLB_SET3_PAGESZ_LOG2_LIST 28 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_ITLB_SET3_ASID_CONSTMASK 0xFF /* constant ASID bits; 0 if all writable */ +#define XCHAL_ITLB_SET3_VPN_CONSTMASK 0xE0000000 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET3_PPN_CONSTMASK 0xF0000000 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_ITLB_SET3_CA_CONSTMASK 0x0000000F /* constant CA bits; 0 if all writable */ +#define XCHAL_ITLB_SET3_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET3_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET3_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_ITLB_SET3_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ +/* Constant ASID values for each entry of ITLB way set 3 (because ASID_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET3_E0_ASID_CONST 0x01 +#define XCHAL_ITLB_SET3_E1_ASID_CONST 0x01 +/* Constant VPN values for each entry of ITLB way set 3 (because VPN_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET3_E0_VPN_CONST 0xE0000000 +#define XCHAL_ITLB_SET3_E1_VPN_CONST 0xF0000000 +/* Constant PPN values for each entry of ITLB way set 3 (because PPN_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET3_E0_PPN_CONST 0xF0000000 +#define XCHAL_ITLB_SET3_E1_PPN_CONST 0xF0000000 +/* Constant CA values for each entry of ITLB way set 3 (because CA_CONSTMASK is non-zero): */ +#define XCHAL_ITLB_SET3_E0_CA_CONST 0x07 +#define XCHAL_ITLB_SET3_E1_CA_CONST 0x03 + +/* Indexing macros: */ +#define _XCHAL_ITLB_SET(n,_what) XCHAL_ITLB_SET ## n ## _what +#define XCHAL_ITLB_SET(n,what) _XCHAL_ITLB_SET(n, _ ## what ) +#define _XCHAL_ITLB_SET_E(n,i,_what) XCHAL_ITLB_SET ## n ## _E ## i ## _what +#define XCHAL_ITLB_SET_E(n,i,what) _XCHAL_ITLB_SET_E(n,i, _ ## what ) +/* + * Example use: XCHAL_ITLB_SET(XCHAL_ITLB_ARF_SET0,ENTRIES) + * to get the value of XCHAL_ITLB_SET_ENTRIES where is the first auto-refill set. + */ + + +/*** Data TLB: ***/ + +#define XCHAL_DTLB_WAY_BITS 4 /* number of bits holding the ways */ +#define XCHAL_DTLB_WAYS 10 /* number of ways (n-way set-associative TLB) */ +#define XCHAL_DTLB_ARF_WAYS 4 /* number of auto-refill ways */ +#define XCHAL_DTLB_SETS 5 /* number of sets (groups of ways with identical settings) */ + +/* Way set to which each way belongs: */ +#define XCHAL_DTLB_WAY0_SET 0 +#define XCHAL_DTLB_WAY1_SET 0 +#define XCHAL_DTLB_WAY2_SET 0 +#define XCHAL_DTLB_WAY3_SET 0 +#define XCHAL_DTLB_WAY4_SET 1 +#define XCHAL_DTLB_WAY5_SET 2 +#define XCHAL_DTLB_WAY6_SET 3 +#define XCHAL_DTLB_WAY7_SET 4 +#define XCHAL_DTLB_WAY8_SET 4 +#define XCHAL_DTLB_WAY9_SET 4 + +/* Ways sets that are used by hardware auto-refill (ARF): */ +#define XCHAL_DTLB_ARF_SETS 1 /* number of auto-refill sets */ +#define XCHAL_DTLB_ARF_SET0 0 /* index of n'th auto-refill set */ + +/* Way sets that are "min-wired" (see terminology comment above): */ +#define XCHAL_DTLB_MINWIRED_SETS 1 /* number of "min-wired" sets */ +#define XCHAL_DTLB_MINWIRED_SET0 4 /* index of n'th "min-wired" set */ + + +/* DTLB way set 0 (group of ways 0 thru 3): */ +#define XCHAL_DTLB_SET0_WAY 0 /* index of first way in this way set */ +#define XCHAL_DTLB_SET0_WAYS 4 /* number of (contiguous) ways in this way set */ +#define XCHAL_DTLB_SET0_ENTRIES_LOG2 2 /* log2(number of entries in this way) */ +#define XCHAL_DTLB_SET0_ENTRIES 4 /* number of entries in this way (always a power of 2) */ +#define XCHAL_DTLB_SET0_ARF 1 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_DTLB_SET0_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_DTLB_SET0_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_DTLB_SET0_PAGESZ_LOG2_MIN 12 /* log2(minimum supported page size) */ +#define XCHAL_DTLB_SET0_PAGESZ_LOG2_MAX 12 /* log2(maximum supported page size) */ +#define XCHAL_DTLB_SET0_PAGESZ_LOG2_LIST 12 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_DTLB_SET0_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_VPN_CONSTMASK 0 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_PPN_CONSTMASK 0 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_DTLB_SET0_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET0_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET0_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET0_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ + +/* DTLB way set 1 (group of ways 4 thru 4): */ +#define XCHAL_DTLB_SET1_WAY 4 /* index of first way in this way set */ +#define XCHAL_DTLB_SET1_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_DTLB_SET1_ENTRIES_LOG2 2 /* log2(number of entries in this way) */ +#define XCHAL_DTLB_SET1_ENTRIES 4 /* number of entries in this way (always a power of 2) */ +#define XCHAL_DTLB_SET1_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_DTLB_SET1_PAGESIZES 4 /* number of supported page sizes in this way */ +#define XCHAL_DTLB_SET1_PAGESZ_BITS 2 /* number of bits to encode the page size */ +#define XCHAL_DTLB_SET1_PAGESZ_LOG2_MIN 20 /* log2(minimum supported page size) */ +#define XCHAL_DTLB_SET1_PAGESZ_LOG2_MAX 26 /* log2(maximum supported page size) */ +#define XCHAL_DTLB_SET1_PAGESZ_LOG2_LIST 20 XCHAL_SEP 22 XCHAL_SEP 24 XCHAL_SEP 26 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_DTLB_SET1_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_DTLB_SET1_VPN_CONSTMASK 0 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET1_PPN_CONSTMASK 0 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET1_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_DTLB_SET1_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET1_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET1_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET1_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ + +/* DTLB way set 2 (group of ways 5 thru 5): */ +#define XCHAL_DTLB_SET2_WAY 5 /* index of first way in this way set */ +#define XCHAL_DTLB_SET2_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_DTLB_SET2_ENTRIES_LOG2 1 /* log2(number of entries in this way) */ +#define XCHAL_DTLB_SET2_ENTRIES 2 /* number of entries in this way (always a power of 2) */ +#define XCHAL_DTLB_SET2_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_DTLB_SET2_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_DTLB_SET2_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_DTLB_SET2_PAGESZ_LOG2_MIN 27 /* log2(minimum supported page size) */ +#define XCHAL_DTLB_SET2_PAGESZ_LOG2_MAX 27 /* log2(maximum supported page size) */ +#define XCHAL_DTLB_SET2_PAGESZ_LOG2_LIST 27 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_DTLB_SET2_ASID_CONSTMASK 0xFF /* constant ASID bits; 0 if all writable */ +#define XCHAL_DTLB_SET2_VPN_CONSTMASK 0xF0000000 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET2_PPN_CONSTMASK 0xF8000000 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET2_CA_CONSTMASK 0x0000000F /* constant CA bits; 0 if all writable */ +#define XCHAL_DTLB_SET2_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET2_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET2_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET2_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ +/* Constant ASID values for each entry of DTLB way set 2 (because ASID_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET2_E0_ASID_CONST 0x01 +#define XCHAL_DTLB_SET2_E1_ASID_CONST 0x01 +/* Constant VPN values for each entry of DTLB way set 2 (because VPN_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET2_E0_VPN_CONST 0xD0000000 +#define XCHAL_DTLB_SET2_E1_VPN_CONST 0xD8000000 +/* Constant PPN values for each entry of DTLB way set 2 (because PPN_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET2_E0_PPN_CONST 0x00000000 +#define XCHAL_DTLB_SET2_E1_PPN_CONST 0x00000000 +/* Constant CA values for each entry of DTLB way set 2 (because CA_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET2_E0_CA_CONST 0x07 +#define XCHAL_DTLB_SET2_E1_CA_CONST 0x03 + +/* DTLB way set 3 (group of ways 6 thru 6): */ +#define XCHAL_DTLB_SET3_WAY 6 /* index of first way in this way set */ +#define XCHAL_DTLB_SET3_WAYS 1 /* number of (contiguous) ways in this way set */ +#define XCHAL_DTLB_SET3_ENTRIES_LOG2 1 /* log2(number of entries in this way) */ +#define XCHAL_DTLB_SET3_ENTRIES 2 /* number of entries in this way (always a power of 2) */ +#define XCHAL_DTLB_SET3_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_DTLB_SET3_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_DTLB_SET3_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_DTLB_SET3_PAGESZ_LOG2_MIN 28 /* log2(minimum supported page size) */ +#define XCHAL_DTLB_SET3_PAGESZ_LOG2_MAX 28 /* log2(maximum supported page size) */ +#define XCHAL_DTLB_SET3_PAGESZ_LOG2_LIST 28 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_DTLB_SET3_ASID_CONSTMASK 0xFF /* constant ASID bits; 0 if all writable */ +#define XCHAL_DTLB_SET3_VPN_CONSTMASK 0xE0000000 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET3_PPN_CONSTMASK 0xF0000000 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET3_CA_CONSTMASK 0x0000000F /* constant CA bits; 0 if all writable */ +#define XCHAL_DTLB_SET3_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET3_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET3_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET3_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ +/* Constant ASID values for each entry of DTLB way set 3 (because ASID_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET3_E0_ASID_CONST 0x01 +#define XCHAL_DTLB_SET3_E1_ASID_CONST 0x01 +/* Constant VPN values for each entry of DTLB way set 3 (because VPN_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET3_E0_VPN_CONST 0xE0000000 +#define XCHAL_DTLB_SET3_E1_VPN_CONST 0xF0000000 +/* Constant PPN values for each entry of DTLB way set 3 (because PPN_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET3_E0_PPN_CONST 0xF0000000 +#define XCHAL_DTLB_SET3_E1_PPN_CONST 0xF0000000 +/* Constant CA values for each entry of DTLB way set 3 (because CA_CONSTMASK is non-zero): */ +#define XCHAL_DTLB_SET3_E0_CA_CONST 0x07 +#define XCHAL_DTLB_SET3_E1_CA_CONST 0x03 + +/* DTLB way set 4 (group of ways 7 thru 9): */ +#define XCHAL_DTLB_SET4_WAY 7 /* index of first way in this way set */ +#define XCHAL_DTLB_SET4_WAYS 3 /* number of (contiguous) ways in this way set */ +#define XCHAL_DTLB_SET4_ENTRIES_LOG2 0 /* log2(number of entries in this way) */ +#define XCHAL_DTLB_SET4_ENTRIES 1 /* number of entries in this way (always a power of 2) */ +#define XCHAL_DTLB_SET4_ARF 0 /* 1=autorefill by h/w, 0=non-autorefill (wired/constant/static) */ +#define XCHAL_DTLB_SET4_PAGESIZES 1 /* number of supported page sizes in this way */ +#define XCHAL_DTLB_SET4_PAGESZ_BITS 0 /* number of bits to encode the page size */ +#define XCHAL_DTLB_SET4_PAGESZ_LOG2_MIN 12 /* log2(minimum supported page size) */ +#define XCHAL_DTLB_SET4_PAGESZ_LOG2_MAX 12 /* log2(maximum supported page size) */ +#define XCHAL_DTLB_SET4_PAGESZ_LOG2_LIST 12 /* list of log2(page size)s, separated by XCHAL_SEP; + 2^PAGESZ_BITS entries in list, unsupported entries are zero */ +#define XCHAL_DTLB_SET4_ASID_CONSTMASK 0 /* constant ASID bits; 0 if all writable */ +#define XCHAL_DTLB_SET4_VPN_CONSTMASK 0 /* constant VPN bits, not including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET4_PPN_CONSTMASK 0 /* constant PPN bits, including entry index bits; 0 if all writable */ +#define XCHAL_DTLB_SET4_CA_CONSTMASK 0 /* constant CA bits; 0 if all writable */ +#define XCHAL_DTLB_SET4_ASID_RESET 0 /* 1 if ASID reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET4_VPN_RESET 0 /* 1 if VPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET4_PPN_RESET 0 /* 1 if PPN reset values defined (and all writable); 0 otherwise */ +#define XCHAL_DTLB_SET4_CA_RESET 0 /* 1 if CA reset values defined (and all writable); 0 otherwise */ + +/* Indexing macros: */ +#define _XCHAL_DTLB_SET(n,_what) XCHAL_DTLB_SET ## n ## _what +#define XCHAL_DTLB_SET(n,what) _XCHAL_DTLB_SET(n, _ ## what ) +#define _XCHAL_DTLB_SET_E(n,i,_what) XCHAL_DTLB_SET ## n ## _E ## i ## _what +#define XCHAL_DTLB_SET_E(n,i,what) _XCHAL_DTLB_SET_E(n,i, _ ## what ) +/* + * Example use: XCHAL_DTLB_SET(XCHAL_DTLB_ARF_SET0,ENTRIES) + * to get the value of XCHAL_DTLB_SET_ENTRIES where is the first auto-refill set. + */ + + +/* + * Determine whether we have a full MMU (with Page Table and Protection) + * usable for an MMU-based OS: + */ +#if XCHAL_HAVE_TLBS && !XCHAL_HAVE_SPANNING_WAY && XCHAL_ITLB_ARF_WAYS > 0 && XCHAL_DTLB_ARF_WAYS > 0 && XCHAL_MMU_RINGS >= 2 +# define XCHAL_HAVE_PTP_MMU 1 /* have full MMU (with page table [autorefill] and protection) */ +#else +# define XCHAL_HAVE_PTP_MMU 0 /* don't have full MMU */ +#endif + +/* + * For full MMUs, report kernel RAM segment and kernel I/O segment static page mappings: + */ +#if XCHAL_HAVE_PTP_MMU +#define XCHAL_KSEG_CACHED_VADDR 0xD0000000 /* virt.addr of kernel RAM cached static map */ +#define XCHAL_KSEG_CACHED_PADDR 0x00000000 /* phys.addr of kseg_cached */ +#define XCHAL_KSEG_CACHED_SIZE 0x08000000 /* size in bytes of kseg_cached (assumed power of 2!!!) */ +#define XCHAL_KSEG_BYPASS_VADDR 0xD8000000 /* virt.addr of kernel RAM bypass (uncached) static map */ +#define XCHAL_KSEG_BYPASS_PADDR 0x00000000 /* phys.addr of kseg_bypass */ +#define XCHAL_KSEG_BYPASS_SIZE 0x08000000 /* size in bytes of kseg_bypass (assumed power of 2!!!) */ + +#define XCHAL_KIO_CACHED_VADDR 0xE0000000 /* virt.addr of kernel I/O cached static map */ +#define XCHAL_KIO_CACHED_PADDR 0xF0000000 /* phys.addr of kio_cached */ +#define XCHAL_KIO_CACHED_SIZE 0x10000000 /* size in bytes of kio_cached (assumed power of 2!!!) */ +#define XCHAL_KIO_BYPASS_VADDR 0xF0000000 /* virt.addr of kernel I/O bypass (uncached) static map */ +#define XCHAL_KIO_BYPASS_PADDR 0xF0000000 /* phys.addr of kio_bypass */ +#define XCHAL_KIO_BYPASS_SIZE 0x10000000 /* size in bytes of kio_bypass (assumed power of 2!!!) */ + +#define XCHAL_SEG_MAPPABLE_VADDR 0x00000000 /* start of largest non-static-mapped virtual addr area */ +#define XCHAL_SEG_MAPPABLE_SIZE 0xD0000000 /* size in bytes of " */ +/* define XCHAL_SEG_MAPPABLE2_xxx if more areas present, sorted in order of descending size. */ +#endif + + +/*---------------------------------------------------------------------- + MISC + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_WRITEBUFFER_ENTRIES 4 /* number of write buffer entries */ + +#define XCHAL_CORE_ID "linux_be" /* configuration's alphanumeric core identifier + (CoreID) set in the Xtensa Processor Generator */ + +#define XCHAL_BUILD_UNIQUE_ID 0x00003256 /* software build-unique ID (22-bit) */ + +/* These definitions describe the hardware targeted by this software: */ +#define XCHAL_HW_CONFIGID0 0xC103D1FF /* config ID reg 0 value (upper 32 of 64 bits) */ +#define XCHAL_HW_CONFIGID1 0x00803256 /* config ID reg 1 value (lower 32 of 64 bits) */ +#define XCHAL_CONFIGID0 XCHAL_HW_CONFIGID0 /* for backward compatibility only -- don't use! */ +#define XCHAL_CONFIGID1 XCHAL_HW_CONFIGID1 /* for backward compatibility only -- don't use! */ +#define XCHAL_HW_RELEASE_MAJOR 1050 /* major release of targeted hardware */ +#define XCHAL_HW_RELEASE_MINOR 1 /* minor release of targeted hardware */ +#define XCHAL_HW_RELEASE_NAME "T1050.1" /* full release name of targeted hardware */ +#define XTHAL_HW_REL_T1050 1 +#define XTHAL_HW_REL_T1050_1 1 +#define XCHAL_HW_CONFIGID_RELIABLE 1 + + +/* + * Miscellaneous special register fields: + */ + + +/* DBREAKC (special register number 160): */ +#define XCHAL_DBREAKC_VALIDMASK 0xC000003F /* bits of DBREAKC that are defined */ +/* MASK field: */ +#define XCHAL_DBREAKC_MASK_BITS 6 /* number of bits in MASK field */ +#define XCHAL_DBREAKC_MASK_NUM 64 /* max number of possible causes (2^bits) */ +#define XCHAL_DBREAKC_MASK_SHIFT 0 /* position of MASK bits in DBREAKC, starting from lsbit */ +#define XCHAL_DBREAKC_MASK_MASK 0x0000003F /* mask of bits in MASK field of DBREAKC */ +/* LOADBREAK field: */ +#define XCHAL_DBREAKC_LOADBREAK_BITS 1 /* number of bits in LOADBREAK field */ +#define XCHAL_DBREAKC_LOADBREAK_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DBREAKC_LOADBREAK_SHIFT 30 /* position of LOADBREAK bits in DBREAKC, starting from lsbit */ +#define XCHAL_DBREAKC_LOADBREAK_MASK 0x40000000 /* mask of bits in LOADBREAK field of DBREAKC */ +/* STOREBREAK field: */ +#define XCHAL_DBREAKC_STOREBREAK_BITS 1 /* number of bits in STOREBREAK field */ +#define XCHAL_DBREAKC_STOREBREAK_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DBREAKC_STOREBREAK_SHIFT 31 /* position of STOREBREAK bits in DBREAKC, starting from lsbit */ +#define XCHAL_DBREAKC_STOREBREAK_MASK 0x80000000 /* mask of bits in STOREBREAK field of DBREAKC */ + +/* PS (special register number 230): */ +#define XCHAL_PS_VALIDMASK 0x00070FFF /* bits of PS that are defined */ +/* INTLEVEL field: */ +#define XCHAL_PS_INTLEVEL_BITS 4 /* number of bits in INTLEVEL field */ +#define XCHAL_PS_INTLEVEL_NUM 16 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_INTLEVEL_SHIFT 0 /* position of INTLEVEL bits in PS, starting from lsbit */ +#define XCHAL_PS_INTLEVEL_MASK 0x0000000F /* mask of bits in INTLEVEL field of PS */ +/* EXCM field: */ +#define XCHAL_PS_EXCM_BITS 1 /* number of bits in EXCM field */ +#define XCHAL_PS_EXCM_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_EXCM_SHIFT 4 /* position of EXCM bits in PS, starting from lsbit */ +#define XCHAL_PS_EXCM_MASK 0x00000010 /* mask of bits in EXCM field of PS */ +/* PROGSTACK field: */ +#define XCHAL_PS_PROGSTACK_BITS 1 /* number of bits in PROGSTACK field */ +#define XCHAL_PS_PROGSTACK_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_PROGSTACK_SHIFT 5 /* position of PROGSTACK bits in PS, starting from lsbit */ +#define XCHAL_PS_PROGSTACK_MASK 0x00000020 /* mask of bits in PROGSTACK field of PS */ +/* RING field: */ +#define XCHAL_PS_RING_BITS 2 /* number of bits in RING field */ +#define XCHAL_PS_RING_NUM 4 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_RING_SHIFT 6 /* position of RING bits in PS, starting from lsbit */ +#define XCHAL_PS_RING_MASK 0x000000C0 /* mask of bits in RING field of PS */ +/* OWB field: */ +#define XCHAL_PS_OWB_BITS 4 /* number of bits in OWB field */ +#define XCHAL_PS_OWB_NUM 16 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_OWB_SHIFT 8 /* position of OWB bits in PS, starting from lsbit */ +#define XCHAL_PS_OWB_MASK 0x00000F00 /* mask of bits in OWB field of PS */ +/* CALLINC field: */ +#define XCHAL_PS_CALLINC_BITS 2 /* number of bits in CALLINC field */ +#define XCHAL_PS_CALLINC_NUM 4 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_CALLINC_SHIFT 16 /* position of CALLINC bits in PS, starting from lsbit */ +#define XCHAL_PS_CALLINC_MASK 0x00030000 /* mask of bits in CALLINC field of PS */ +/* WOE field: */ +#define XCHAL_PS_WOE_BITS 1 /* number of bits in WOE field */ +#define XCHAL_PS_WOE_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_PS_WOE_SHIFT 18 /* position of WOE bits in PS, starting from lsbit */ +#define XCHAL_PS_WOE_MASK 0x00040000 /* mask of bits in WOE field of PS */ + +/* EXCCAUSE (special register number 232): */ +#define XCHAL_EXCCAUSE_VALIDMASK 0x0000003F /* bits of EXCCAUSE that are defined */ +/* EXCCAUSE field: */ +#define XCHAL_EXCCAUSE_BITS 6 /* number of bits in EXCCAUSE register */ +#define XCHAL_EXCCAUSE_NUM 64 /* max number of possible causes (2^bits) */ +#define XCHAL_EXCCAUSE_SHIFT 0 /* position of EXCCAUSE bits in register, starting from lsbit */ +#define XCHAL_EXCCAUSE_MASK 0x0000003F /* mask of bits in EXCCAUSE register */ + +/* DEBUGCAUSE (special register number 233): */ +#define XCHAL_DEBUGCAUSE_VALIDMASK 0x0000003F /* bits of DEBUGCAUSE that are defined */ +/* ICOUNT field: */ +#define XCHAL_DEBUGCAUSE_ICOUNT_BITS 1 /* number of bits in ICOUNT field */ +#define XCHAL_DEBUGCAUSE_ICOUNT_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DEBUGCAUSE_ICOUNT_SHIFT 0 /* position of ICOUNT bits in DEBUGCAUSE, starting from lsbit */ +#define XCHAL_DEBUGCAUSE_ICOUNT_MASK 0x00000001 /* mask of bits in ICOUNT field of DEBUGCAUSE */ +/* IBREAK field: */ +#define XCHAL_DEBUGCAUSE_IBREAK_BITS 1 /* number of bits in IBREAK field */ +#define XCHAL_DEBUGCAUSE_IBREAK_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DEBUGCAUSE_IBREAK_SHIFT 1 /* position of IBREAK bits in DEBUGCAUSE, starting from lsbit */ +#define XCHAL_DEBUGCAUSE_IBREAK_MASK 0x00000002 /* mask of bits in IBREAK field of DEBUGCAUSE */ +/* DBREAK field: */ +#define XCHAL_DEBUGCAUSE_DBREAK_BITS 1 /* number of bits in DBREAK field */ +#define XCHAL_DEBUGCAUSE_DBREAK_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DEBUGCAUSE_DBREAK_SHIFT 2 /* position of DBREAK bits in DEBUGCAUSE, starting from lsbit */ +#define XCHAL_DEBUGCAUSE_DBREAK_MASK 0x00000004 /* mask of bits in DBREAK field of DEBUGCAUSE */ +/* BREAK field: */ +#define XCHAL_DEBUGCAUSE_BREAK_BITS 1 /* number of bits in BREAK field */ +#define XCHAL_DEBUGCAUSE_BREAK_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DEBUGCAUSE_BREAK_SHIFT 3 /* position of BREAK bits in DEBUGCAUSE, starting from lsbit */ +#define XCHAL_DEBUGCAUSE_BREAK_MASK 0x00000008 /* mask of bits in BREAK field of DEBUGCAUSE */ +/* BREAKN field: */ +#define XCHAL_DEBUGCAUSE_BREAKN_BITS 1 /* number of bits in BREAKN field */ +#define XCHAL_DEBUGCAUSE_BREAKN_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DEBUGCAUSE_BREAKN_SHIFT 4 /* position of BREAKN bits in DEBUGCAUSE, starting from lsbit */ +#define XCHAL_DEBUGCAUSE_BREAKN_MASK 0x00000010 /* mask of bits in BREAKN field of DEBUGCAUSE */ +/* DEBUGINT field: */ +#define XCHAL_DEBUGCAUSE_DEBUGINT_BITS 1 /* number of bits in DEBUGINT field */ +#define XCHAL_DEBUGCAUSE_DEBUGINT_NUM 2 /* max number of possible causes (2^bits) */ +#define XCHAL_DEBUGCAUSE_DEBUGINT_SHIFT 5 /* position of DEBUGINT bits in DEBUGCAUSE, starting from lsbit */ +#define XCHAL_DEBUGCAUSE_DEBUGINT_MASK 0x00000020 /* mask of bits in DEBUGINT field of DEBUGCAUSE */ + + + +/*---------------------------------------------------------------------- + ISA + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_DENSITY 1 /* 1 if density option configured, 0 otherwise */ +#define XCHAL_HAVE_LOOPS 1 /* 1 if zero-overhead loops option configured, 0 otherwise */ +/* Misc instructions: */ +#define XCHAL_HAVE_NSA 0 /* 1 if NSA/NSAU instructions option configured, 0 otherwise */ +#define XCHAL_HAVE_MINMAX 0 /* 1 if MIN/MAX instructions option configured, 0 otherwise */ +#define XCHAL_HAVE_SEXT 0 /* 1 if sign-extend instruction option configured, 0 otherwise */ +#define XCHAL_HAVE_CLAMPS 0 /* 1 if CLAMPS instruction option configured, 0 otherwise */ +#define XCHAL_HAVE_MAC16 0 /* 1 if MAC16 option configured, 0 otherwise */ +#define XCHAL_HAVE_MUL16 0 /* 1 if 16-bit integer multiply option configured, 0 otherwise */ +/*#define XCHAL_HAVE_POPC 0*/ /* 1 if CRC instruction option configured, 0 otherwise */ +/*#define XCHAL_HAVE_CRC 0*/ /* 1 if POPC instruction option configured, 0 otherwise */ + +#define XCHAL_HAVE_SPECULATION 0 /* 1 if speculation option configured, 0 otherwise */ +/*#define XCHAL_HAVE_MP_SYNC 0*/ /* 1 if multiprocessor sync. option configured, 0 otherwise */ +#define XCHAL_HAVE_PRID 0 /* 1 if processor ID register configured, 0 otherwise */ + +#define XCHAL_NUM_MISC_REGS 2 /* number of miscellaneous registers (0..4) */ + +/* These relate a bit more to TIE: */ +#define XCHAL_HAVE_BOOLEANS 0 /* 1 if booleans option configured, 0 otherwise */ +#define XCHAL_HAVE_MUL32 0 /* 1 if 32-bit integer multiply option configured, 0 otherwise */ +#define XCHAL_HAVE_MUL32_HIGH 0 /* 1 if MUL32 option includes MULUH and MULSH, 0 otherwise */ +#define XCHAL_HAVE_FP 0 /* 1 if floating point option configured, 0 otherwise */ + + +/*---------------------------------------------------------------------- + DERIVED + ----------------------------------------------------------------------*/ + +#if XCHAL_HAVE_BE +#define XCHAL_INST_ILLN 0xD60F /* 2-byte illegal instruction, msb-first */ +#define XCHAL_INST_ILLN_BYTE0 0xD6 /* 2-byte illegal instruction, 1st byte */ +#define XCHAL_INST_ILLN_BYTE1 0x0F /* 2-byte illegal instruction, 2nd byte */ +#else +#define XCHAL_INST_ILLN 0xF06D /* 2-byte illegal instruction, lsb-first */ +#define XCHAL_INST_ILLN_BYTE0 0x6D /* 2-byte illegal instruction, 1st byte */ +#define XCHAL_INST_ILLN_BYTE1 0xF0 /* 2-byte illegal instruction, 2nd byte */ +#endif +/* Belongs in xtensa/hal.h: */ +#define XTHAL_INST_ILL 0x000000 /* 3-byte illegal instruction */ + + +/* + * Because information as to exactly which hardware release is targeted + * by a given software build is not always available, compile-time HAL + * Hardware-Release "_AT" macros are fuzzy (return 0, 1, or XCHAL_MAYBE): + */ +#ifndef XCHAL_HW_RELEASE_MAJOR +# define XCHAL_HW_CONFIGID_RELIABLE 0 +#endif +#if XCHAL_HW_CONFIGID_RELIABLE +# define XCHAL_HW_RELEASE_AT_OR_BELOW(major,minor) (XTHAL_REL_LE( XCHAL_HW_RELEASE_MAJOR,XCHAL_HW_RELEASE_MINOR, major,minor ) ? 1 : 0) +# define XCHAL_HW_RELEASE_AT_OR_ABOVE(major,minor) (XTHAL_REL_GE( XCHAL_HW_RELEASE_MAJOR,XCHAL_HW_RELEASE_MINOR, major,minor ) ? 1 : 0) +# define XCHAL_HW_RELEASE_AT(major,minor) (XTHAL_REL_EQ( XCHAL_HW_RELEASE_MAJOR,XCHAL_HW_RELEASE_MINOR, major,minor ) ? 1 : 0) +# define XCHAL_HW_RELEASE_MAJOR_AT(major) ((XCHAL_HW_RELEASE_MAJOR == (major)) ? 1 : 0) +#else +# define XCHAL_HW_RELEASE_AT_OR_BELOW(major,minor) ( ((major) < 1040 && XCHAL_HAVE_XEA2) ? 0 \ + : ((major) > 1050 && XCHAL_HAVE_XEA1) ? 1 \ + : XTHAL_MAYBE ) +# define XCHAL_HW_RELEASE_AT_OR_ABOVE(major,minor) ( ((major) >= 2000 && XCHAL_HAVE_XEA1) ? 0 \ + : (XTHAL_REL_LE(major,minor, 1040,0) && XCHAL_HAVE_XEA2) ? 1 \ + : XTHAL_MAYBE ) +# define XCHAL_HW_RELEASE_AT(major,minor) ( (((major) < 1040 && XCHAL_HAVE_XEA2) || \ + ((major) >= 2000 && XCHAL_HAVE_XEA1)) ? 0 : XTHAL_MAYBE) +# define XCHAL_HW_RELEASE_MAJOR_AT(major) XCHAL_HW_RELEASE_AT(major,0) +#endif + +/* + * Specific errata: + */ + +/* + * Erratum T1020.H13, T1030.H7, T1040.H10, T1050.H4 (fixed in T1040.3 and T1050.1; + * relevant only in XEA1, kernel-vector mode, level-one interrupts and overflows enabled): + */ +#define XCHAL_MAYHAVE_ERRATUM_XEA1KWIN (XCHAL_HAVE_XEA1 && \ + (XCHAL_HW_RELEASE_AT_OR_BELOW(1040,2) != 0 \ + || XCHAL_HW_RELEASE_AT(1050,0))) + + + +#endif /*XTENSA_CONFIG_CORE_H*/ + diff --git a/include/asm-xtensa/xtensa/config-linux_be/defs.h b/include/asm-xtensa/xtensa/config-linux_be/defs.h new file mode 100644 index 000000000000..f7c58b273371 --- /dev/null +++ b/include/asm-xtensa/xtensa/config-linux_be/defs.h @@ -0,0 +1,270 @@ +/* Definitions for Xtensa instructions, types, and protos. */ + +/* + * Copyright (c) 2003 Tensilica, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2.1 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + +/* Do not modify. This is automatically generated.*/ + +#ifndef _XTENSA_BASE_HEADER +#define _XTENSA_BASE_HEADER + +#ifdef __XTENSA__ +#if defined(__GNUC__) && !defined(__XCC__) + +#define L8UI_ASM(arr, ars, imm) { \ + __asm__ volatile("l8ui %0, %1, %2" : "=a" (arr) : "a" (ars) , "i" (imm)); \ +} + +#define XT_L8UI(ars, imm) \ +({ \ + unsigned char _arr; \ + const unsigned char *_ars = ars; \ + L8UI_ASM(_arr, _ars, imm); \ + _arr; \ +}) + +#define L16UI_ASM(arr, ars, imm) { \ + __asm__ volatile("l16ui %0, %1, %2" : "=a" (arr) : "a" (ars) , "i" (imm)); \ +} + +#define XT_L16UI(ars, imm) \ +({ \ + unsigned short _arr; \ + const unsigned short *_ars = ars; \ + L16UI_ASM(_arr, _ars, imm); \ + _arr; \ +}) + +#define L16SI_ASM(arr, ars, imm) {\ + __asm__ volatile("l16si %0, %1, %2" : "=a" (arr) : "a" (ars) , "i" (imm)); \ +} + +#define XT_L16SI(ars, imm) \ +({ \ + signed short _arr; \ + const signed short *_ars = ars; \ + L16SI_ASM(_arr, _ars, imm); \ + _arr; \ +}) + +#define L32I_ASM(arr, ars, imm) { \ + __asm__ volatile("l32i %0, %1, %2" : "=a" (arr) : "a" (ars) , "i" (imm)); \ +} + +#define XT_L32I(ars, imm) \ +({ \ + unsigned _arr; \ + const unsigned *_ars = ars; \ + L32I_ASM(_arr, _ars, imm); \ + _arr; \ +}) + +#define S8I_ASM(arr, ars, imm) {\ + __asm__ volatile("s8i %0, %1, %2" : : "a" (arr), "a" (ars) , "i" (imm) : "memory" ); \ +} + +#define XT_S8I(arr, ars, imm) \ +({ \ + signed char _arr = arr; \ + const signed char *_ars = ars; \ + S8I_ASM(_arr, _ars, imm); \ +}) + +#define S16I_ASM(arr, ars, imm) {\ + __asm__ volatile("s16i %0, %1, %2" : : "a" (arr), "a" (ars) , "i" (imm) : "memory" ); \ +} + +#define XT_S16I(arr, ars, imm) \ +({ \ + signed short _arr = arr; \ + const signed short *_ars = ars; \ + S16I_ASM(_arr, _ars, imm); \ +}) + +#define S32I_ASM(arr, ars, imm) { \ + __asm__ volatile("s32i %0, %1, %2" : : "a" (arr), "a" (ars) , "i" (imm) : "memory" ); \ +} + +#define XT_S32I(arr, ars, imm) \ +({ \ + signed int _arr = arr; \ + const signed int *_ars = ars; \ + S32I_ASM(_arr, _ars, imm); \ +}) + +#define ADDI_ASM(art, ars, imm) {\ + __asm__ ("addi %0, %1, %2" : "=a" (art) : "a" (ars), "i" (imm)); \ +} + +#define XT_ADDI(ars, imm) \ +({ \ + unsigned _art; \ + unsigned _ars = ars; \ + ADDI_ASM(_art, _ars, imm); \ + _art; \ +}) + +#define ABS_ASM(arr, art) {\ + __asm__ ("abs %0, %1" : "=a" (arr) : "a" (art)); \ +} + +#define XT_ABS(art) \ +({ \ + unsigned _arr; \ + signed _art = art; \ + ABS_ASM(_arr, _art); \ + _arr; \ +}) + +/* Note: In the following macros that reference SAR, the magic "state" + register is used to capture the dependency on SAR. This is because + SAR is a 5-bit register and thus there are no C types that can be + used to represent it. It doesn't appear that the SAR register is + even relevant to GCC, but it is marked as "clobbered" just in + case. */ + +#define SRC_ASM(arr, ars, art) {\ + register int _xt_sar __asm__ ("state"); \ + __asm__ ("src %0, %1, %2" \ + : "=a" (arr) : "a" (ars), "a" (art), "t" (_xt_sar)); \ +} + +#define XT_SRC(ars, art) \ +({ \ + unsigned _arr; \ + unsigned _ars = ars; \ + unsigned _art = art; \ + SRC_ASM(_arr, _ars, _art); \ + _arr; \ +}) + +#define SSR_ASM(ars) {\ + register int _xt_sar __asm__ ("state"); \ + __asm__ ("ssr %1" : "=t" (_xt_sar) : "a" (ars) : "sar"); \ +} + +#define XT_SSR(ars) \ +({ \ + unsigned _ars = ars; \ + SSR_ASM(_ars); \ +}) + +#define SSL_ASM(ars) {\ + register int _xt_sar __asm__ ("state"); \ + __asm__ ("ssl %1" : "=t" (_xt_sar) : "a" (ars) : "sar"); \ +} + +#define XT_SSL(ars) \ +({ \ + unsigned _ars = ars; \ + SSL_ASM(_ars); \ +}) + +#define SSA8B_ASM(ars) {\ + register int _xt_sar __asm__ ("state"); \ + __asm__ ("ssa8b %1" : "=t" (_xt_sar) : "a" (ars) : "sar"); \ +} + +#define XT_SSA8B(ars) \ +({ \ + unsigned _ars = ars; \ + SSA8B_ASM(_ars); \ +}) + +#define SSA8L_ASM(ars) {\ + register int _xt_sar __asm__ ("state"); \ + __asm__ ("ssa8l %1" : "=t" (_xt_sar) : "a" (ars) : "sar"); \ +} + +#define XT_SSA8L(ars) \ +({ \ + unsigned _ars = ars; \ + SSA8L_ASM(_ars); \ +}) + +#define SSAI_ASM(imm) {\ + register int _xt_sar __asm__ ("state"); \ + __asm__ ("ssai %1" : "=t" (_xt_sar) : "i" (imm) : "sar"); \ +} + +#define XT_SSAI(imm) \ +({ \ + SSAI_ASM(imm); \ +}) + + + + + + + + +#endif /* __GNUC__ && !__XCC__ */ + +#ifdef __XCC__ + +/* Core load/store instructions */ +extern unsigned char _TIE_L8UI(const unsigned char * ars, immediate imm); +extern unsigned short _TIE_L16UI(const unsigned short * ars, immediate imm); +extern signed short _TIE_L16SI(const signed short * ars, immediate imm); +extern unsigned _TIE_L32I(const unsigned * ars, immediate imm); +extern void _TIE_S8I(unsigned char arr, unsigned char * ars, immediate imm); +extern void _TIE_S16I(unsigned short arr, unsigned short * ars, immediate imm); +extern void _TIE_S32I(unsigned arr, unsigned * ars, immediate imm); + +#define XT_L8UI _TIE_L8UI +#define XT_L16UI _TIE_L16UI +#define XT_L16SI _TIE_L16SI +#define XT_L32I _TIE_L32I +#define XT_S8I _TIE_S8I +#define XT_S16I _TIE_S16I +#define XT_S32I _TIE_S32I + +/* Add-immediate instruction */ +extern unsigned _TIE_ADDI(unsigned ars, immediate imm); +#define XT_ADDI _TIE_ADDI + +/* Absolute value instruction */ +extern unsigned _TIE_ABS(int art); +#define XT_ABS _TIE_ABS + +/* funnel shift instructions */ +extern unsigned _TIE_SRC(unsigned ars, unsigned art); +#define XT_SRC _TIE_SRC +extern void _TIE_SSR(unsigned ars); +#define XT_SSR _TIE_SSR +extern void _TIE_SSL(unsigned ars); +#define XT_SSL _TIE_SSL +extern void _TIE_SSA8B(unsigned ars); +#define XT_SSA8B _TIE_SSA8B +extern void _TIE_SSA8L(unsigned ars); +#define XT_SSA8L _TIE_SSA8L +extern void _TIE_SSAI(immediate imm); +#define XT_SSAI _TIE_SSAI + + +#endif /* __XCC__ */ + +#endif /* __XTENSA__ */ +#endif /* !_XTENSA_BASE_HEADER */ diff --git a/include/asm-xtensa/xtensa/config-linux_be/specreg.h b/include/asm-xtensa/xtensa/config-linux_be/specreg.h new file mode 100644 index 000000000000..fa4106aa9a02 --- /dev/null +++ b/include/asm-xtensa/xtensa/config-linux_be/specreg.h @@ -0,0 +1,99 @@ +/* + * Xtensa Special Register symbolic names + */ + +/* $Id: specreg.h,v 1.2 2003/03/07 19:15:18 joetaylor Exp $ */ + +/* + * Copyright (c) 2003 Tensilica, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2.1 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + +#ifndef XTENSA_SPECREG_H +#define XTENSA_SPECREG_H + +/* Include these special register bitfield definitions, for historical reasons: */ +#include + + +/* Special registers: */ +#define LBEG 0 +#define LEND 1 +#define LCOUNT 2 +#define SAR 3 +#define WINDOWBASE 72 +#define WINDOWSTART 73 +#define PTEVADDR 83 +#define RASID 90 +#define ITLBCFG 91 +#define DTLBCFG 92 +#define IBREAKENABLE 96 +#define DDR 104 +#define IBREAKA_0 128 +#define IBREAKA_1 129 +#define DBREAKA_0 144 +#define DBREAKA_1 145 +#define DBREAKC_0 160 +#define DBREAKC_1 161 +#define EPC_1 177 +#define EPC_2 178 +#define EPC_3 179 +#define EPC_4 180 +#define DEPC 192 +#define EPS_2 194 +#define EPS_3 195 +#define EPS_4 196 +#define EXCSAVE_1 209 +#define EXCSAVE_2 210 +#define EXCSAVE_3 211 +#define EXCSAVE_4 212 +#define INTERRUPT 226 +#define INTENABLE 228 +#define PS 230 +#define EXCCAUSE 232 +#define DEBUGCAUSE 233 +#define CCOUNT 234 +#define ICOUNT 236 +#define ICOUNTLEVEL 237 +#define EXCVADDR 238 +#define CCOMPARE_0 240 +#define CCOMPARE_1 241 +#define CCOMPARE_2 242 +#define MISC_REG_0 244 +#define MISC_REG_1 245 + +/* Special cases (bases of special register series): */ +#define IBREAKA 128 +#define DBREAKA 144 +#define DBREAKC 160 +#define EPC 176 +#define EPS 192 +#define EXCSAVE 208 +#define CCOMPARE 240 + +/* Special names for read-only and write-only interrupt registers: */ +#define INTREAD 226 +#define INTSET 226 +#define INTCLEAR 227 + +#endif /* XTENSA_SPECREG_H */ + diff --git a/include/asm-xtensa/xtensa/config-linux_be/system.h b/include/asm-xtensa/xtensa/config-linux_be/system.h new file mode 100644 index 000000000000..cf9d4d308e3a --- /dev/null +++ b/include/asm-xtensa/xtensa/config-linux_be/system.h @@ -0,0 +1,198 @@ +/* + * xtensa/config/system.h -- HAL definitions that are dependent on SYSTEM configuration + * + * NOTE: The location and contents of this file are highly subject to change. + * + * Source for configuration-independent binaries (which link in a + * configuration-specific HAL library) must NEVER include this file. + * The HAL itself has historically included this file in some instances, + * but this is not appropriate either, because the HAL is meant to be + * core-specific but system independent. + */ + +/* + * Copyright (c) 2003 Tensilica, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2.1 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + + +#ifndef XTENSA_CONFIG_SYSTEM_H +#define XTENSA_CONFIG_SYSTEM_H + +/*#include */ + + + +/*---------------------------------------------------------------------- + DEVICE ADDRESSES + ----------------------------------------------------------------------*/ + +/* + * Strange place to find these, but the configuration GUI + * allows moving these around to account for various core + * configurations. Specific boards (and their BSP software) + * will have specific meanings for these components. + */ + +/* I/O Block areas: */ +#define XSHAL_IOBLOCK_CACHED_VADDR 0xE0000000 +#define XSHAL_IOBLOCK_CACHED_PADDR 0xF0000000 +#define XSHAL_IOBLOCK_CACHED_SIZE 0x0E000000 + +#define XSHAL_IOBLOCK_BYPASS_VADDR 0xF0000000 +#define XSHAL_IOBLOCK_BYPASS_PADDR 0xF0000000 +#define XSHAL_IOBLOCK_BYPASS_SIZE 0x0E000000 + +/* System ROM: */ +#define XSHAL_ROM_VADDR 0xEE000000 +#define XSHAL_ROM_PADDR 0xFE000000 +#define XSHAL_ROM_SIZE 0x00400000 +/* Largest available area (free of vectors): */ +#define XSHAL_ROM_AVAIL_VADDR 0xEE00052C +#define XSHAL_ROM_AVAIL_VSIZE 0x003FFAD4 + +/* System RAM: */ +#define XSHAL_RAM_VADDR 0xD0000000 +#define XSHAL_RAM_PADDR 0x00000000 +#define XSHAL_RAM_VSIZE 0x08000000 +#define XSHAL_RAM_PSIZE 0x10000000 +#define XSHAL_RAM_SIZE XSHAL_RAM_PSIZE +/* Largest available area (free of vectors): */ +#define XSHAL_RAM_AVAIL_VADDR 0xD0000370 +#define XSHAL_RAM_AVAIL_VSIZE 0x07FFFC90 + +/* + * Shadow system RAM (same device as system RAM, at different address). + * (Emulation boards need this for the SONIC Ethernet driver + * when data caches are configured for writeback mode.) + * NOTE: on full MMU configs, this points to the BYPASS virtual address + * of system RAM, ie. is the same as XSHAL_RAM_* except that virtual + * addresses are viewed through the BYPASS static map rather than + * the CACHED static map. + */ +#define XSHAL_RAM_BYPASS_VADDR 0xD8000000 +#define XSHAL_RAM_BYPASS_PADDR 0x00000000 +#define XSHAL_RAM_BYPASS_PSIZE 0x08000000 + +/* Alternate system RAM (different device than system RAM): */ +#define XSHAL_ALTRAM_VADDR 0xCEE00000 +#define XSHAL_ALTRAM_PADDR 0xC0000000 +#define XSHAL_ALTRAM_SIZE 0x00200000 + + +/*---------------------------------------------------------------------- + * DEVICE-ADDRESS DEPENDENT... + * + * Values written to CACHEATTR special register (or its equivalent) + * to enable and disable caches in various modes. + *----------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------- + BACKWARD COMPATIBILITY ... + ----------------------------------------------------------------------*/ + +/* + * NOTE: the following two macros are DEPRECATED. Use the latter + * board-specific macros instead, which are specially tuned for the + * particular target environments' memory maps. + */ +#define XSHAL_CACHEATTR_BYPASS XSHAL_XT2000_CACHEATTR_BYPASS /* disable caches in bypass mode */ +#define XSHAL_CACHEATTR_DEFAULT XSHAL_XT2000_CACHEATTR_DEFAULT /* default setting to enable caches (no writeback!) */ + +/*---------------------------------------------------------------------- + ISS (Instruction Set Simulator) SPECIFIC ... + ----------------------------------------------------------------------*/ + +#define XSHAL_ISS_CACHEATTR_WRITEBACK 0x1122222F /* enable caches in write-back mode */ +#define XSHAL_ISS_CACHEATTR_WRITEALLOC 0x1122222F /* enable caches in write-allocate mode */ +#define XSHAL_ISS_CACHEATTR_WRITETHRU 0x1122222F /* enable caches in write-through mode */ +#define XSHAL_ISS_CACHEATTR_BYPASS 0x2222222F /* disable caches in bypass mode */ +#define XSHAL_ISS_CACHEATTR_DEFAULT XSHAL_ISS_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +/* For Coware only: */ +#define XSHAL_COWARE_CACHEATTR_WRITEBACK 0x11222222 /* enable caches in write-back mode */ +#define XSHAL_COWARE_CACHEATTR_WRITEALLOC 0x11222222 /* enable caches in write-allocate mode */ +#define XSHAL_COWARE_CACHEATTR_WRITETHRU 0x11222222 /* enable caches in write-through mode */ +#define XSHAL_COWARE_CACHEATTR_BYPASS 0x22222222 /* disable caches in bypass mode */ +#define XSHAL_COWARE_CACHEATTR_DEFAULT XSHAL_COWARE_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +/* For BFM and other purposes: */ +#define XSHAL_ALLVALID_CACHEATTR_WRITEBACK 0x11222222 /* enable caches without any invalid regions */ +#define XSHAL_ALLVALID_CACHEATTR_DEFAULT XSHAL_ALLVALID_CACHEATTR_WRITEBACK /* default setting for caches without any invalid regions */ + +#define XSHAL_ISS_PIPE_REGIONS 0 +#define XSHAL_ISS_SDRAM_REGIONS 0 + + +/*---------------------------------------------------------------------- + XT2000 BOARD SPECIFIC ... + ----------------------------------------------------------------------*/ + +#define XSHAL_XT2000_CACHEATTR_WRITEBACK 0x22FFFFFF /* enable caches in write-back mode */ +#define XSHAL_XT2000_CACHEATTR_WRITEALLOC 0x22FFFFFF /* enable caches in write-allocate mode */ +#define XSHAL_XT2000_CACHEATTR_WRITETHRU 0x22FFFFFF /* enable caches in write-through mode */ +#define XSHAL_XT2000_CACHEATTR_BYPASS 0x22FFFFFF /* disable caches in bypass mode */ +#define XSHAL_XT2000_CACHEATTR_DEFAULT XSHAL_XT2000_CACHEATTR_WRITEBACK /* default setting to enable caches */ + +#define XSHAL_XT2000_PIPE_REGIONS 0x00001000 /* BusInt pipeline regions */ +#define XSHAL_XT2000_SDRAM_REGIONS 0x00000005 /* BusInt SDRAM regions */ + + +/*---------------------------------------------------------------------- + VECTOR SIZES + ----------------------------------------------------------------------*/ + +/* + * Sizes allocated to vectors by the system (memory map) configuration. + * These sizes are constrained by core configuration (eg. one vector's + * code cannot overflow into another vector) but are dependent on the + * system or board (or LSP) memory map configuration. + * + * Whether or not each vector happens to be in a system ROM is also + * a system configuration matter, sometimes useful, included here also: + */ +#define XSHAL_RESET_VECTOR_SIZE 0x000004E0 +#define XSHAL_RESET_VECTOR_ISROM 1 +#define XSHAL_USER_VECTOR_SIZE 0x0000001C +#define XSHAL_USER_VECTOR_ISROM 0 +#define XSHAL_PROGRAMEXC_VECTOR_SIZE XSHAL_USER_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_USEREXC_VECTOR_SIZE XSHAL_USER_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_KERNEL_VECTOR_SIZE 0x0000001C +#define XSHAL_KERNEL_VECTOR_ISROM 0 +#define XSHAL_STACKEDEXC_VECTOR_SIZE XSHAL_KERNEL_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_KERNELEXC_VECTOR_SIZE XSHAL_KERNEL_VECTOR_SIZE /* for backward compatibility */ +#define XSHAL_DOUBLEEXC_VECTOR_SIZE 0x000000E0 +#define XSHAL_DOUBLEEXC_VECTOR_ISROM 0 +#define XSHAL_WINDOW_VECTORS_SIZE 0x00000180 +#define XSHAL_WINDOW_VECTORS_ISROM 0 +#define XSHAL_INTLEVEL2_VECTOR_SIZE 0x0000000C +#define XSHAL_INTLEVEL2_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL3_VECTOR_SIZE 0x0000000C +#define XSHAL_INTLEVEL3_VECTOR_ISROM 0 +#define XSHAL_INTLEVEL4_VECTOR_SIZE 0x0000000C +#define XSHAL_INTLEVEL4_VECTOR_ISROM 1 +#define XSHAL_DEBUG_VECTOR_SIZE XSHAL_INTLEVEL4_VECTOR_SIZE +#define XSHAL_DEBUG_VECTOR_ISROM XSHAL_INTLEVEL4_VECTOR_ISROM + + +#endif /*XTENSA_CONFIG_SYSTEM_H*/ + diff --git a/include/asm-xtensa/xtensa/config-linux_be/tie.h b/include/asm-xtensa/xtensa/config-linux_be/tie.h new file mode 100644 index 000000000000..3c2e514602f4 --- /dev/null +++ b/include/asm-xtensa/xtensa/config-linux_be/tie.h @@ -0,0 +1,275 @@ +/* + * xtensa/config/tie.h -- HAL definitions that are dependent on CORE and TIE configuration + * + * This header file is sometimes referred to as the "compile-time HAL" or CHAL. + * It was generated for a specific Xtensa processor configuration, + * and furthermore for a specific set of TIE source files that extend + * basic core functionality. + * + * Source for configuration-independent binaries (which link in a + * configuration-specific HAL library) must NEVER include this file. + * It is perfectly normal, however, for the HAL source itself to include this file. + */ + +/* + * Copyright (c) 2003 Tensilica, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2.1 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + + +#ifndef XTENSA_CONFIG_TIE_H +#define XTENSA_CONFIG_TIE_H + +#include + + +/*---------------------------------------------------------------------- + GENERAL + ----------------------------------------------------------------------*/ + +/* + * Separators for macros that expand into arrays. + * These can be predefined by files that #include this one, + * when different separators are required. + */ +/* Element separator for macros that expand into 1-dimensional arrays: */ +#ifndef XCHAL_SEP +#define XCHAL_SEP , +#endif +/* Array separator for macros that expand into 2-dimensional arrays: */ +#ifndef XCHAL_SEP2 +#define XCHAL_SEP2 },{ +#endif + + + + + + +/*---------------------------------------------------------------------- + COPROCESSORS and EXTRA STATE + ----------------------------------------------------------------------*/ + +#define XCHAL_CP_NUM 0 /* number of coprocessors */ +#define XCHAL_CP_MAX 0 /* max coprocessor id plus one (0 if none) */ +#define XCHAL_CP_MASK 0x00 /* bitmask of coprocessors by id */ + +/* Space for coprocessors' state save areas: */ +#define XCHAL_CP0_SA_SIZE 0 +#define XCHAL_CP1_SA_SIZE 0 +#define XCHAL_CP2_SA_SIZE 0 +#define XCHAL_CP3_SA_SIZE 0 +#define XCHAL_CP4_SA_SIZE 0 +#define XCHAL_CP5_SA_SIZE 0 +#define XCHAL_CP6_SA_SIZE 0 +#define XCHAL_CP7_SA_SIZE 0 +/* Minimum required alignments of CP state save areas: */ +#define XCHAL_CP0_SA_ALIGN 1 +#define XCHAL_CP1_SA_ALIGN 1 +#define XCHAL_CP2_SA_ALIGN 1 +#define XCHAL_CP3_SA_ALIGN 1 +#define XCHAL_CP4_SA_ALIGN 1 +#define XCHAL_CP5_SA_ALIGN 1 +#define XCHAL_CP6_SA_ALIGN 1 +#define XCHAL_CP7_SA_ALIGN 1 + +/* Indexing macros: */ +#define _XCHAL_CP_SA_SIZE(n) XCHAL_CP ## n ## _SA_SIZE +#define XCHAL_CP_SA_SIZE(n) _XCHAL_CP_SA_SIZE(n) /* n = 0 .. 7 */ +#define _XCHAL_CP_SA_ALIGN(n) XCHAL_CP ## n ## _SA_ALIGN +#define XCHAL_CP_SA_ALIGN(n) _XCHAL_CP_SA_ALIGN(n) /* n = 0 .. 7 */ + + +/* Space for "extra" state (user special registers and non-cp TIE) save area: */ +#define XCHAL_EXTRA_SA_SIZE 0 +#define XCHAL_EXTRA_SA_ALIGN 1 + +/* Total save area size (extra + all coprocessors) */ +/* (not useful until xthal_{save,restore}_all_extra() is implemented, */ +/* but included for Tor2 beta; doesn't account for alignment!): */ +#define XCHAL_CPEXTRA_SA_SIZE_TOR2 0 /* Tor2Beta temporary definition -- do not use */ + +/* Combined required alignment for all CP and EXTRA state save areas */ +/* (does not include required alignment for any base config registers): */ +#define XCHAL_CPEXTRA_SA_ALIGN 1 + +/* ... */ + + +#ifdef _ASMLANGUAGE +/* + * Assembly-language specific definitions (assembly macros, etc.). + */ +#include + +/******************** + * Macros to save and restore the non-coprocessor TIE portion of EXTRA state. + */ + +/* (none) */ + + +/******************** + * Macros to create functions that save and restore all EXTRA (non-coprocessor) state + * (does not include zero-overhead loop registers and non-optional registers). + */ + + /* + * Macro that expands to the body of a function that + * stores the extra (non-coprocessor) optional/custom state. + * Entry: a2 = ptr to save area in which to save extra state + * Exit: any register a2-a15 (?) may have been clobbered. + */ + .macro xchal_extra_store_funcbody + .endm + + + /* + * Macro that expands to the body of a function that + * loads the extra (non-coprocessor) optional/custom state. + * Entry: a2 = ptr to save area from which to restore extra state + * Exit: any register a2-a15 (?) may have been clobbered. + */ + .macro xchal_extra_load_funcbody + .endm + + +/******************** + * Macros to save and restore the state of each TIE coprocessor. + */ + + + +/******************** + * Macros to create functions that save and restore the state of *any* TIE coprocessor. + */ + + /* + * Macro that expands to the body of a function + * that stores the selected coprocessor's state (registers etc). + * Entry: a2 = ptr to save area in which to save cp state + * a3 = coprocessor number + * Exit: any register a2-a15 (?) may have been clobbered. + */ + .macro xchal_cpi_store_funcbody + .endm + + + /* + * Macro that expands to the body of a function + * that loads the selected coprocessor's state (registers etc). + * Entry: a2 = ptr to save area from which to restore cp state + * a3 = coprocessor number + * Exit: any register a2-a15 (?) may have been clobbered. + */ + .macro xchal_cpi_load_funcbody + .endm + +#endif /*_ASMLANGUAGE*/ + + +/* + * Contents of save areas in terms of libdb register numbers. + * NOTE: CONTENTS_LIBDB_{UREG,REGF} macros are not defined in this file; + * it is up to the user of this header file to define these macros + * usefully before each expansion of the CONTENTS_LIBDB macros. + * (Fields rsv[123] are reserved for future additions; they are currently + * set to zero but may be set to some useful values in the future.) + * + * CONTENTS_LIBDB_SREG(libdbnum, offset, size, align, rsv1, name, sregnum, bitmask, rsv2, rsv3) + * CONTENTS_LIBDB_UREG(libdbnum, offset, size, align, rsv1, name, uregnum, bitmask, rsv2, rsv3) + * CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index, numentries, contentsize, regname_base, regfile_name, rsv2, rsv3) + */ + +#define XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_EXTRA_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP0_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP0_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP1_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP1_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP2_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP2_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP3_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP3_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP4_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP4_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP5_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP5_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP6_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP6_SA_CONTENTS_LIBDB /* empty */ + +#define XCHAL_CP7_SA_CONTENTS_LIBDB_NUM 0 +#define XCHAL_CP7_SA_CONTENTS_LIBDB /* empty */ + + + + + + +/*---------------------------------------------------------------------- + MISC + ----------------------------------------------------------------------*/ + +#if 0 /* is there something equivalent for user TIE? */ +#define XCHAL_CORE_ID "linux_be" /* configuration's alphanumeric core identifier + (CoreID) set in the Xtensa Processor Generator */ + +#define XCHAL_BUILD_UNIQUE_ID 0x00003256 /* software build-unique ID (22-bit) */ + +/* These definitions describe the hardware targeted by this software: */ +#define XCHAL_HW_CONFIGID0 0xC103D1FF /* config ID reg 0 value (upper 32 of 64 bits) */ +#define XCHAL_HW_CONFIGID1 0x00803256 /* config ID reg 1 value (lower 32 of 64 bits) */ +#define XCHAL_CONFIGID0 XCHAL_HW_CONFIGID0 /* for backward compatibility only -- don't use! */ +#define XCHAL_CONFIGID1 XCHAL_HW_CONFIGID1 /* for backward compatibility only -- don't use! */ +#define XCHAL_HW_RELEASE_MAJOR 1050 /* major release of targeted hardware */ +#define XCHAL_HW_RELEASE_MINOR 1 /* minor release of targeted hardware */ +#define XCHAL_HW_RELEASE_NAME "T1050.1" /* full release name of targeted hardware */ +#define XTHAL_HW_REL_T1050 1 +#define XTHAL_HW_REL_T1050_1 1 +#define XCHAL_HW_CONFIGID_RELIABLE 1 +#endif /*0*/ + + + +/*---------------------------------------------------------------------- + ISA + ----------------------------------------------------------------------*/ + +#if 0 /* these probably don't belong here, but are related to or implemented using TIE */ +#define XCHAL_HAVE_BOOLEANS 0 /* 1 if booleans option configured, 0 otherwise */ +/* Misc instructions: */ +#define XCHAL_HAVE_MUL32 0 /* 1 if 32-bit integer multiply option configured, 0 otherwise */ +#define XCHAL_HAVE_MUL32_HIGH 0 /* 1 if MUL32 option includes MULUH and MULSH, 0 otherwise */ + +#define XCHAL_HAVE_FP 0 /* 1 if floating point option configured, 0 otherwise */ +#endif /*0*/ + + +#endif /*XTENSA_CONFIG_TIE_H*/ + diff --git a/include/asm-xtensa/xtensa/coreasm.h b/include/asm-xtensa/xtensa/coreasm.h new file mode 100644 index 000000000000..a8cfb54c20a1 --- /dev/null +++ b/include/asm-xtensa/xtensa/coreasm.h @@ -0,0 +1,526 @@ +#ifndef XTENSA_COREASM_H +#define XTENSA_COREASM_H + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * include/asm-xtensa/xtensa/coreasm.h -- assembler-specific + * definitions that depend on CORE configuration. + * + * Source for configuration-independent binaries (which link in a + * configuration-specific HAL library) must NEVER include this file. + * It is perfectly normal, however, for the HAL itself to include this + * file. + * + * This file must NOT include xtensa/config/system.h. Any assembler + * header file that depends on system information should likely go in + * a new systemasm.h (or sysasm.h) header file. + * + * NOTE: macro beqi32 is NOT configuration-dependent, and is placed + * here til we will have configuration-independent header file. + * + * 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) 2002 Tensilica Inc. + */ + + +#include +#include + +/* + * Assembly-language specific definitions (assembly macros, etc.). + */ + +/*---------------------------------------------------------------------- + * find_ms_setbit + * + * This macro finds the most significant bit that is set in + * and return its index + in , or - 1 if is zero. + * The index counts starting at zero for the lsbit, so the return + * value ranges from -1 (no bit set) to +31 (msbit set). + * + * Parameters: + * destination address register (any register) + * source address register + * temporary address register (must be different than ) + * constant value added to result (usually 0 or 1) + * On entry: + * = undefined if different than + * = value whose most significant set bit is to be found + * = undefined + * no other registers are used by this macro. + * On exit: + * = + index of msbit set in original , + * = - 1 if original was zero. + * clobbered (if not ) + * clobbered (if not ) + * Example: + * find_ms_setbit a0, a4, a0, 0 -- return in a0 index of msbit set in a4 + */ + + .macro find_ms_setbit ad, as, at, base +#if XCHAL_HAVE_NSA + movi \at, 31+\base + nsau \as, \as // get index of \as, numbered from msbit (32 if absent) + sub \ad, \at, \as // get numbering from lsbit (0..31, -1 if absent) +#else /* XCHAL_HAVE_NSA */ + movi \at, \base // start with result of 0 (point to lsbit of 32) + + beqz \as, 2f // special case for zero argument: return -1 + bltui \as, 0x10000, 1f // is it one of the 16 lsbits? (if so, check lower 16 bits) + addi \at, \at, 16 // no, increment result to upper 16 bits (of 32) + //srli \as, \as, 16 // check upper half (shift right 16 bits) + extui \as, \as, 16, 16 // check upper half (shift right 16 bits) +1: bltui \as, 0x100, 1f // is it one of the 8 lsbits? (if so, check lower 8 bits) + addi \at, \at, 8 // no, increment result to upper 8 bits (of 16) + srli \as, \as, 8 // shift right to check upper 8 bits +1: bltui \as, 0x10, 1f // is it one of the 4 lsbits? (if so, check lower 4 bits) + addi \at, \at, 4 // no, increment result to upper 4 bits (of 8) + srli \as, \as, 4 // shift right 4 bits to check upper half +1: bltui \as, 0x4, 1f // is it one of the 2 lsbits? (if so, check lower 2 bits) + addi \at, \at, 2 // no, increment result to upper 2 bits (of 4) + srli \as, \as, 2 // shift right 2 bits to check upper half +1: bltui \as, 0x2, 1f // is it the lsbit? + addi \at, \at, 2 // no, increment result to upper bit (of 2) +2: addi \at, \at, -1 // (from just above: add 1; from beqz: return -1) + //srli \as, \as, 1 +1: // done! \at contains index of msbit set (or -1 if none set) + .if 0x\ad - 0x\at // destination different than \at ? (works because regs are a0-a15) + mov \ad, \at // then move result to \ad + .endif +#endif /* XCHAL_HAVE_NSA */ + .endm // find_ms_setbit + +/*---------------------------------------------------------------------- + * find_ls_setbit + * + * This macro finds the least significant bit that is set in , + * and return its index in . + * Usage is the same as for the find_ms_setbit macro. + * Example: + * find_ls_setbit a0, a4, a0, 0 -- return in a0 index of lsbit set in a4 + */ + + .macro find_ls_setbit ad, as, at, base + neg \at, \as // keep only the least-significant bit that is set... + and \as, \at, \as // ... in \as + find_ms_setbit \ad, \as, \at, \base + .endm // find_ls_setbit + +/*---------------------------------------------------------------------- + * find_ls_one + * + * Same as find_ls_setbit with base zero. + * Source (as) and destination (ad) registers must be different. + * Provided for backward compatibility. + */ + + .macro find_ls_one ad, as + find_ls_setbit \ad, \as, \ad, 0 + .endm // find_ls_one + +/*---------------------------------------------------------------------- + * floop, floopnez, floopgtz, floopend + * + * These macros are used for fast inner loops that + * work whether or not the Loops options is configured. + * If the Loops option is configured, they simply use + * the zero-overhead LOOP instructions; otherwise + * they use explicit decrement and branch instructions. + * + * They are used in pairs, with floop, floopnez or floopgtz + * at the beginning of the loop, and floopend at the end. + * + * Each pair of loop macro calls must be given the loop count + * address register and a unique label for that loop. + * + * Example: + * + * movi a3, 16 // loop 16 times + * floop a3, myloop1 + * : + * bnez a7, end1 // exit loop if a7 != 0 + * : + * floopend a3, myloop1 + * end1: + * + * Like the LOOP instructions, these macros cannot be + * nested, must include at least one instruction, + * cannot call functions inside the loop, etc. + * The loop can be exited by jumping to the instruction + * following floopend (or elsewhere outside the loop), + * or continued by jumping to a NOP instruction placed + * immediately before floopend. + * + * Unlike LOOP instructions, the register passed to floop* + * cannot be used inside the loop, because it is used as + * the loop counter if the Loops option is not configured. + * And its value is undefined after exiting the loop. + * And because the loop counter register is active inside + * the loop, you can't easily use this construct to loop + * across a register file using ROTW as you might with LOOP + * instructions, unless you copy the loop register along. + */ + + /* Named label version of the macros: */ + + .macro floop ar, endlabel + floop_ \ar, .Lfloopstart_\endlabel, .Lfloopend_\endlabel + .endm + + .macro floopnez ar, endlabel + floopnez_ \ar, .Lfloopstart_\endlabel, .Lfloopend_\endlabel + .endm + + .macro floopgtz ar, endlabel + floopgtz_ \ar, .Lfloopstart_\endlabel, .Lfloopend_\endlabel + .endm + + .macro floopend ar, endlabel + floopend_ \ar, .Lfloopstart_\endlabel, .Lfloopend_\endlabel + .endm + + /* Numbered local label version of the macros: */ +#if 0 /*UNTESTED*/ + .macro floop89 ar + floop_ \ar, 8, 9f + .endm + + .macro floopnez89 ar + floopnez_ \ar, 8, 9f + .endm + + .macro floopgtz89 ar + floopgtz_ \ar, 8, 9f + .endm + + .macro floopend89 ar + floopend_ \ar, 8b, 9 + .endm +#endif /*0*/ + + /* Underlying version of the macros: */ + + .macro floop_ ar, startlabel, endlabelref + .ifdef _infloop_ + .if _infloop_ + .err // Error: floop cannot be nested + .endif + .endif + .set _infloop_, 1 +#if XCHAL_HAVE_LOOPS + loop \ar, \endlabelref +#else /* XCHAL_HAVE_LOOPS */ +\startlabel: + addi \ar, \ar, -1 +#endif /* XCHAL_HAVE_LOOPS */ + .endm // floop_ + + .macro floopnez_ ar, startlabel, endlabelref + .ifdef _infloop_ + .if _infloop_ + .err // Error: floopnez cannot be nested + .endif + .endif + .set _infloop_, 1 +#if XCHAL_HAVE_LOOPS + loopnez \ar, \endlabelref +#else /* XCHAL_HAVE_LOOPS */ + beqz \ar, \endlabelref +\startlabel: + addi \ar, \ar, -1 +#endif /* XCHAL_HAVE_LOOPS */ + .endm // floopnez_ + + .macro floopgtz_ ar, startlabel, endlabelref + .ifdef _infloop_ + .if _infloop_ + .err // Error: floopgtz cannot be nested + .endif + .endif + .set _infloop_, 1 +#if XCHAL_HAVE_LOOPS + loopgtz \ar, \endlabelref +#else /* XCHAL_HAVE_LOOPS */ + bltz \ar, \endlabelref + beqz \ar, \endlabelref +\startlabel: + addi \ar, \ar, -1 +#endif /* XCHAL_HAVE_LOOPS */ + .endm // floopgtz_ + + + .macro floopend_ ar, startlabelref, endlabel + .ifndef _infloop_ + .err // Error: floopend without matching floopXXX + .endif + .ifeq _infloop_ + .err // Error: floopend without matching floopXXX + .endif + .set _infloop_, 0 +#if ! XCHAL_HAVE_LOOPS + bnez \ar, \startlabelref +#endif /* XCHAL_HAVE_LOOPS */ +\endlabel: + .endm // floopend_ + +/*---------------------------------------------------------------------- + * crsil -- conditional RSIL (read/set interrupt level) + * + * Executes the RSIL instruction if it exists, else just reads PS. + * The RSIL instruction does not exist in the new exception architecture + * if the interrupt option is not selected. + */ + + .macro crsil ar, newlevel +#if XCHAL_HAVE_OLD_EXC_ARCH || XCHAL_HAVE_INTERRUPTS + rsil \ar, \newlevel +#else + rsr \ar, PS +#endif + .endm // crsil + +/*---------------------------------------------------------------------- + * window_spill{4,8,12} + * + * These macros spill callers' register windows to the stack. + * They work for both privileged and non-privileged tasks. + * Must be called from a windowed ABI context, eg. within + * a windowed ABI function (ie. valid stack frame, window + * exceptions enabled, not in exception mode, etc). + * + * This macro requires a single invocation of the window_spill_common + * macro in the same assembly unit and section. + * + * Note that using window_spill{4,8,12} macros is more efficient + * than calling a function implemented using window_spill_function, + * because the latter needs extra code to figure out the size of + * the call to the spilling function. + * + * Example usage: + * + * .text + * .align 4 + * .global some_function + * .type some_function,@function + * some_function: + * entry a1, 16 + * : + * : + * + * window_spill4 // spill windows of some_function's callers; preserves a0..a3 only; + * // to use window_spill{8,12} in this example function we'd have + * // to increase space allocated by the entry instruction, because + * // 16 bytes only allows call4; 32 or 48 bytes (+locals) are needed + * // for call8/window_spill8 or call12/window_spill12 respectively. + * : + * + * retw + * + * window_spill_common // instantiates code used by window_spill4 + * + * + * On entry: + * none (if window_spill4) + * stack frame has enough space allocated for call8 (if window_spill8) + * stack frame has enough space allocated for call12 (if window_spill12) + * On exit: + * a4..a15 clobbered (if window_spill4) + * a8..a15 clobbered (if window_spill8) + * a12..a15 clobbered (if window_spill12) + * no caller windows are in live registers + */ + + .macro window_spill4 +#if XCHAL_HAVE_WINDOWED +# if XCHAL_NUM_AREGS == 16 + movi a15, 0 // for 16-register files, no need to call to reach the end +# elif XCHAL_NUM_AREGS == 32 + call4 .L__wdwspill_assist28 // call deep enough to clear out any live callers +# elif XCHAL_NUM_AREGS == 64 + call4 .L__wdwspill_assist60 // call deep enough to clear out any live callers +# endif +#endif + .endm // window_spill4 + + .macro window_spill8 +#if XCHAL_HAVE_WINDOWED +# if XCHAL_NUM_AREGS == 16 + movi a15, 0 // for 16-register files, no need to call to reach the end +# elif XCHAL_NUM_AREGS == 32 + call8 .L__wdwspill_assist24 // call deep enough to clear out any live callers +# elif XCHAL_NUM_AREGS == 64 + call8 .L__wdwspill_assist56 // call deep enough to clear out any live callers +# endif +#endif + .endm // window_spill8 + + .macro window_spill12 +#if XCHAL_HAVE_WINDOWED +# if XCHAL_NUM_AREGS == 16 + movi a15, 0 // for 16-register files, no need to call to reach the end +# elif XCHAL_NUM_AREGS == 32 + call12 .L__wdwspill_assist20 // call deep enough to clear out any live callers +# elif XCHAL_NUM_AREGS == 64 + call12 .L__wdwspill_assist52 // call deep enough to clear out any live callers +# endif +#endif + .endm // window_spill12 + +/*---------------------------------------------------------------------- + * window_spill_function + * + * This macro outputs a function that will spill its caller's callers' + * register windows to the stack. Eg. it could be used to implement + * a version of xthal_window_spill() that works in non-privileged tasks. + * This works for both privileged and non-privileged tasks. + * + * Typical usage: + * + * .text + * .align 4 + * .global my_spill_function + * .type my_spill_function,@function + * my_spill_function: + * window_spill_function + * + * On entry to resulting function: + * none + * On exit from resulting function: + * none (no caller windows are in live registers) + */ + + .macro window_spill_function +#if XCHAL_HAVE_WINDOWED +# if XCHAL_NUM_AREGS == 32 + entry sp, 48 + bbci.l a0, 31, 1f // branch if called with call4 + bbsi.l a0, 30, 2f // branch if called with call12 + call8 .L__wdwspill_assist16 // called with call8, only need another 8 + retw +1: call12 .L__wdwspill_assist16 // called with call4, only need another 12 + retw +2: call4 .L__wdwspill_assist16 // called with call12, only need another 4 + retw +# elif XCHAL_NUM_AREGS == 64 + entry sp, 48 + bbci.l a0, 31, 1f // branch if called with call4 + bbsi.l a0, 30, 2f // branch if called with call12 + call4 .L__wdwspill_assist52 // called with call8, only need a call4 + retw +1: call8 .L__wdwspill_assist52 // called with call4, only need a call8 + retw +2: call12 .L__wdwspill_assist40 // called with call12, can skip a call12 + retw +# elif XCHAL_NUM_AREGS == 16 + entry sp, 16 + bbci.l a0, 31, 1f // branch if called with call4 + bbsi.l a0, 30, 2f // branch if called with call12 + movi a7, 0 // called with call8 + retw +1: movi a11, 0 // called with call4 +2: retw // if called with call12, everything already spilled + +// movi a15, 0 // trick to spill all but the direct caller +// j 1f +// // The entry instruction is magical in the assembler (gets auto-aligned) +// // so we have to jump to it to avoid falling through the padding. +// // We need entry/retw to know where to return. +//1: entry sp, 16 +// retw +# else +# error "unrecognized address register file size" +# endif +#endif /* XCHAL_HAVE_WINDOWED */ + window_spill_common + .endm // window_spill_function + +/*---------------------------------------------------------------------- + * window_spill_common + * + * Common code used by any number of invocations of the window_spill## + * and window_spill_function macros. + * + * Must be instantiated exactly once within a given assembly unit, + * within call/j range of and same section as window_spill## + * macro invocations for that assembly unit. + * (Is automatically instantiated by the window_spill_function macro.) + */ + + .macro window_spill_common +#if XCHAL_HAVE_WINDOWED && (XCHAL_NUM_AREGS == 32 || XCHAL_NUM_AREGS == 64) + .ifndef .L__wdwspill_defined +# if XCHAL_NUM_AREGS >= 64 +.L__wdwspill_assist60: + entry sp, 32 + call8 .L__wdwspill_assist52 + retw +.L__wdwspill_assist56: + entry sp, 16 + call4 .L__wdwspill_assist52 + retw +.L__wdwspill_assist52: + entry sp, 48 + call12 .L__wdwspill_assist40 + retw +.L__wdwspill_assist40: + entry sp, 48 + call12 .L__wdwspill_assist28 + retw +# endif +.L__wdwspill_assist28: + entry sp, 48 + call12 .L__wdwspill_assist16 + retw +.L__wdwspill_assist24: + entry sp, 32 + call8 .L__wdwspill_assist16 + retw +.L__wdwspill_assist20: + entry sp, 16 + call4 .L__wdwspill_assist16 + retw +.L__wdwspill_assist16: + entry sp, 16 + movi a15, 0 + retw + .set .L__wdwspill_defined, 1 + .endif +#endif /* XCHAL_HAVE_WINDOWED with 32 or 64 aregs */ + .endm // window_spill_common + +/*---------------------------------------------------------------------- + * beqi32 + * + * macro implements version of beqi for arbitrary 32-bit immidiate value + * + * beqi32 ax, ay, imm32, label + * + * Compares value in register ax with imm32 value and jumps to label if + * equal. Clobberes register ay if needed + * + */ + .macro beqi32 ax, ay, imm, label + .ifeq ((\imm-1) & ~7) // 1..8 ? + beqi \ax, \imm, \label + .else + .ifeq (\imm+1) // -1 ? + beqi \ax, \imm, \label + .else + .ifeq (\imm) // 0 ? + beqz \ax, \label + .else + // We could also handle immediates 10,12,16,32,64,128,256 + // but it would be a long macro... + movi \ay, \imm + beq \ax, \ay, \label + .endif + .endif + .endif + .endm // beqi32 + +#endif /*XTENSA_COREASM_H*/ + diff --git a/include/asm-xtensa/xtensa/corebits.h b/include/asm-xtensa/xtensa/corebits.h new file mode 100644 index 000000000000..e578ade41632 --- /dev/null +++ b/include/asm-xtensa/xtensa/corebits.h @@ -0,0 +1,77 @@ +#ifndef XTENSA_COREBITS_H +#define XTENSA_COREBITS_H + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * xtensa/corebits.h - Xtensa Special Register field positions and masks. + * + * (In previous releases, these were defined in specreg.h, a generated file. + * This file is not generated, i.e. it is processor configuration independent.) + */ + + +/* EXCCAUSE register fields: */ +#define EXCCAUSE_EXCCAUSE_SHIFT 0 +#define EXCCAUSE_EXCCAUSE_MASK 0x3F +/* Exception causes (mostly incomplete!): */ +#define EXCCAUSE_ILLEGAL 0 +#define EXCCAUSE_SYSCALL 1 +#define EXCCAUSE_IFETCHERROR 2 +#define EXCCAUSE_LOADSTOREERROR 3 +#define EXCCAUSE_LEVEL1INTERRUPT 4 +#define EXCCAUSE_ALLOCA 5 + +/* PS register fields: */ +#define PS_WOE_SHIFT 18 +#define PS_WOE_MASK 0x00040000 +#define PS_WOE PS_WOE_MASK +#define PS_CALLINC_SHIFT 16 +#define PS_CALLINC_MASK 0x00030000 +#define PS_CALLINC(n) (((n)&3)<4) 0 2 or >3 (TBD) + * T1030.0 0 1 (HAL beta) + * T1030.{1,2} 0 3 Equivalent to first release. + * T1030.n (n>=3) 0 >= 3 (TBD) + * T1040.n 1040 n Full CHAL available from T1040.2 + * T1050.n 1050 n Current release. + * + * + * Note: there is a distinction between the software release with + * which something is compiled (accessible using XTHAL_RELEASE_* macros) + * and the software release with which the HAL library was compiled + * (accessible using Xthal_release_* global variables). This + * distinction is particularly relevant for vendors that distribute + * configuration-independent binaries (eg. an OS), where their customer + * might link it with a HAL of a different Xtensa software release. + * In this case, it may be appropriate for the OS to verify at run-time + * whether XTHAL_RELEASE_* and Xthal_release_* are compatible. + * [Guidelines as to which release is compatible with which are not + * currently provided explicitly, but might be inferred from reading + * OSKit documentation for all releases -- compatibility is also highly + * dependent on which HAL features are used. Each release is usually + * backward compatible, with very few exceptions if any.] + * + * Notes: + * Tornado 2.0 supported in T1020.3+, T1030.1+, and T1040.{0,1} only. + * Tornado 2.0.2 supported in T1040.2+, and T1050. + * Compile-time HAL port of NucleusPlus supported by T1040.2+ and T1050. + */ + + +/* + * Architectural limits, independent of configuration. + * Note that these are ISA-defined limits, not micro-architecture implementation + * limits enforced by the Xtensa Processor Generator (which may be stricter than + * these below). + */ +#define XTHAL_MAX_CPS 8 /* max number of coprocessors (0..7) */ +#define XTHAL_MAX_INTERRUPTS 32 /* max number of interrupts (0..31) */ +#define XTHAL_MAX_INTLEVELS 16 /* max number of interrupt levels (0..15) */ + /* (as of T1040, implementation limit is 7: 0..6) */ +#define XTHAL_MAX_TIMERS 4 /* max number of timers (CCOMPARE0..CCOMPARE3) */ + /* (as of T1040, implementation limit is 3: 0..2) */ + +/* Misc: */ +#define XTHAL_LITTLEENDIAN 0 +#define XTHAL_BIGENDIAN 1 + + +/* Interrupt types: */ +#define XTHAL_INTTYPE_UNCONFIGURED 0 +#define XTHAL_INTTYPE_SOFTWARE 1 +#define XTHAL_INTTYPE_EXTERN_EDGE 2 +#define XTHAL_INTTYPE_EXTERN_LEVEL 3 +#define XTHAL_INTTYPE_TIMER 4 +#define XTHAL_INTTYPE_NMI 5 +#define XTHAL_MAX_INTTYPES 6 /* number of interrupt types */ + +/* Timer related: */ +#define XTHAL_TIMER_UNCONFIGURED -1 /* Xthal_timer_interrupt[] value for non-existent timers */ +#define XTHAL_TIMER_UNASSIGNED XTHAL_TIMER_UNCONFIGURED /* (for backwards compatibility only) */ + + +/* Access Mode bits (tentative): */ /* bit abbr unit short_name PPC equ - Description */ +#define XTHAL_AMB_EXCEPTION 0 /* 001 E EX fls: EXception none - generate exception on any access (aka "illegal") */ +#define XTHAL_AMB_HITCACHE 1 /* 002 C CH fls: use Cache on Hit ~(I CI) - use cache on hit -- way from tag match [or H HC, or U UC] (ISA: same, except for Isolate case) */ +#define XTHAL_AMB_ALLOCATE 2 /* 004 A AL fl?: ALlocate none - refill cache on miss -- way from LRU [or F FI fill] (ISA: Read/Write Miss Refill) */ +#define XTHAL_AMB_WRITETHRU 3 /* 008 W WT --s: WriteThrough W WT - store immediately to memory (ISA: same) */ +#define XTHAL_AMB_ISOLATE 4 /* 010 I IS fls: ISolate none - use cache regardless of hit-vs-miss -- way from vaddr (ISA: use-cache-on-miss+hit) */ +#define XTHAL_AMB_GUARD 5 /* 020 G GU ?l?: GUard G * - non-speculative; spec/replay refs not permitted */ +#if 0 +#define XTHAL_AMB_ORDERED x /* 000 O OR fls: ORdered G * - mem accesses cannot be out of order */ +#define XTHAL_AMB_FUSEWRITES x /* 000 F FW --s: FuseWrites none - allow combining/merging multiple writes (to same datapath data unit) into one (implied by writeback) */ +#define XTHAL_AMB_COHERENT x /* 000 M MC fl?: Mem/MP Coherent M - on reads, other CPUs/bus-masters may need to supply data */ +#define XTHAL_AMB_TRUSTED x /* 000 T TR ?l?: TRusted none - memory will not bus error (if it does, handle as fatal imprecise interrupt) */ +#define XTHAL_AMB_PREFETCH x /* 000 P PR fl?: PRefetch none - on refill, read line+1 into prefetch buffers */ +#define XTHAL_AMB_STREAM x /* 000 S ST ???: STreaming none - access one of N stream buffers */ +#endif /*0*/ + +#define XTHAL_AM_EXCEPTION (1< = bit is set + * '-' = bit is clear + * '.' = bit is irrelevant / don't care, as follows: + * E=1 makes all others irrelevant + * W,F relevant only for stores + * "2345" + * Indicates which Xtensa releases support the corresponding + * access mode. Releases for each character column are: + * 2 = prior to T1020.2: T1015 (V1.5), T1020.0, T1020.1 + * 3 = T1020.2 and later: T1020.2+, T1030 + * 4 = T1040 + * 5 = T1050 (maybe) + * And the character column contents are: + * = support by release(s) + * "." = unsupported by release(s) + * "?" = support unknown + */ + /* FOGIWACE 2345 */ +/* For instruction fetch: */ +#define XTHAL_FAM_EXCEPTION 0x001 /* .......E 2345 exception */ +#define XTHAL_FAM_ISOLATE 0x012 /* .--I.-C- .... isolate */ +#define XTHAL_FAM_BYPASS 0x000 /* .---.--- 2345 bypass */ +#define XTHAL_FAM_NACACHED 0x002 /* .---.-C- .... cached no-allocate (frozen) */ +#define XTHAL_FAM_CACHED 0x006 /* .---.AC- 2345 cached */ +/* For data load: */ +#define XTHAL_LAM_EXCEPTION 0x001 /* .......E 2345 exception */ +#define XTHAL_LAM_ISOLATE 0x012 /* .--I.-C- 2345 isolate */ +#define XTHAL_LAM_BYPASS 0x000 /* .O--.--- 2... bypass speculative */ +#define XTHAL_LAM_BYPASSG 0x020 /* .OG-.--- .345 bypass guarded */ +#define XTHAL_LAM_NACACHED 0x002 /* .O--.-C- 2... cached no-allocate speculative */ +#define XTHAL_LAM_NACACHEDG 0x022 /* .OG-.-C- .345 cached no-allocate guarded */ +#define XTHAL_LAM_CACHED 0x006 /* .---.AC- 2345 cached speculative */ +#define XTHAL_LAM_CACHEDG 0x026 /* .?G-.AC- .... cached guarded */ +/* For data store: */ +#define XTHAL_SAM_EXCEPTION 0x001 /* .......E 2345 exception */ +#define XTHAL_SAM_ISOLATE 0x032 /* .-GI--C- 2345 isolate */ +#define XTHAL_SAM_BYPASS 0x028 /* -OG-W--- 2345 bypass */ +/*efine XTHAL_SAM_BYPASSF 0x028*/ /* F-G-W--- ...? bypass write-combined */ +#define XTHAL_SAM_WRITETHRU 0x02A /* -OG-W-C- 234? writethrough */ +/*efine XTHAL_SAM_WRITETHRUF 0x02A*/ /* F-G-W-C- ...5 writethrough write-combined */ +#define XTHAL_SAM_WRITEALLOC 0x02E /* -OG-WAC- ...? writethrough-allocate */ +/*efine XTHAL_SAM_WRITEALLOCF 0x02E*/ /* F-G-WAC- ...? writethrough-allocate write-combined */ +#define XTHAL_SAM_WRITEBACK 0x026 /* F-G--AC- ...5 writeback */ + +#if 0 +/* + Cache attribute encoding for CACHEATTR (per ISA): + (Note: if this differs from ISA Ref Manual, ISA has precedence) + + Inst-fetches Loads Stores + ------------- ------------ ------------- +0x0 FCA_EXCEPTION ?LCA_NACACHED_G* SCA_WRITETHRU "uncached" +0x1 FCA_CACHED LCA_CACHED SCA_WRITETHRU cached +0x2 FCA_BYPASS LCA_BYPASS_G* SCA_BYPASS bypass +0x3 FCA_CACHED LCA_CACHED SCA_WRITEALLOCF write-allocate + or LCA_EXCEPTION SCA_EXCEPTION (if unimplemented) +0x4 FCA_CACHED LCA_CACHED SCA_WRITEBACK write-back + or LCA_EXCEPTION SCA_EXCEPTION (if unimplemented) +0x5..D FCA_EXCEPTION LCA_EXCEPTION SCA_EXCEPTION (reserved) +0xE FCA_EXCEPTION LCA_ISOLATE SCA_ISOLATE isolate +0xF FCA_EXCEPTION LCA_EXCEPTION SCA_EXCEPTION illegal + * Prior to T1020.2?, guard feature not supported, this defaulted to speculative (no _G) +*/ +#endif /*0*/ + + +#if !defined(__ASSEMBLY__) && !defined(_NOCLANGUAGE) +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------------------- + HAL + ----------------------------------------------------------------------*/ + +/* Constant to be checked in build = (XTHAL_MAJOR_REV<<16)|XTHAL_MINOR_REV */ +extern const unsigned int Xthal_rev_no; + + +/*---------------------------------------------------------------------- + Processor State + ----------------------------------------------------------------------*/ +/* save & restore the extra processor state */ +extern void xthal_save_extra(void *base); +extern void xthal_restore_extra(void *base); + +extern void xthal_save_cpregs(void *base, int); +extern void xthal_restore_cpregs(void *base, int); + +/*extern void xthal_save_all_extra(void *base);*/ +/*extern void xthal_restore_all_extra(void *base);*/ + +/* space for processor state */ +extern const unsigned int Xthal_extra_size; +extern const unsigned int Xthal_extra_align; +/* space for TIE register files */ +extern const unsigned int Xthal_cpregs_size[XTHAL_MAX_CPS]; +extern const unsigned int Xthal_cpregs_align[XTHAL_MAX_CPS]; + +/* total of space for the processor state (for Tor2) */ +extern const unsigned int Xthal_all_extra_size; +extern const unsigned int Xthal_all_extra_align; + +/* initialize the extra processor */ +/*extern void xthal_init_extra(void);*/ +/* initialize the TIE coprocessor */ +/*extern void xthal_init_cp(int);*/ + +/* initialize the extra processor */ +extern void xthal_init_mem_extra(void *); +/* initialize the TIE coprocessor */ +extern void xthal_init_mem_cp(void *, int); + +/* validate & invalidate the TIE register file */ +extern void xthal_validate_cp(int); +extern void xthal_invalidate_cp(int); + +/* the number of TIE coprocessors contiguous from zero (for Tor2) */ +extern const unsigned int Xthal_num_coprocessors; + +/* actual number of coprocessors */ +extern const unsigned char Xthal_cp_num; +/* index of highest numbered coprocessor, plus one */ +extern const unsigned char Xthal_cp_max; +/* index of highest allowed coprocessor number, per cfg, plus one */ +/*extern const unsigned char Xthal_cp_maxcfg;*/ +/* bitmask of which coprocessors are present */ +extern const unsigned int Xthal_cp_mask; + +/* read and write cpenable register */ +extern void xthal_set_cpenable(unsigned); +extern unsigned xthal_get_cpenable(void); + +/* read & write extra state register */ +/*extern int xthal_read_extra(void *base, unsigned reg, unsigned *value);*/ +/*extern int xthal_write_extra(void *base, unsigned reg, unsigned value);*/ + +/* read & write a TIE coprocessor register */ +/*extern int xthal_read_cpreg(void *base, int cp, unsigned reg, unsigned *value);*/ +/*extern int xthal_write_cpreg(void *base, int cp, unsigned reg, unsigned value);*/ + +/* return coprocessor number based on register */ +/*extern int xthal_which_cp(unsigned reg);*/ + +/*---------------------------------------------------------------------- + Interrupts + ----------------------------------------------------------------------*/ + +/* the number of interrupt levels */ +extern const unsigned char Xthal_num_intlevels; +/* the number of interrupts */ +extern const unsigned char Xthal_num_interrupts; + +/* mask for level of interrupts */ +extern const unsigned int Xthal_intlevel_mask[XTHAL_MAX_INTLEVELS]; +/* mask for level 0 to N interrupts */ +extern const unsigned int Xthal_intlevel_andbelow_mask[XTHAL_MAX_INTLEVELS]; + +/* level of each interrupt */ +extern const unsigned char Xthal_intlevel[XTHAL_MAX_INTERRUPTS]; + +/* type per interrupt */ +extern const unsigned char Xthal_inttype[XTHAL_MAX_INTERRUPTS]; + +/* masks of each type of interrupt */ +extern const unsigned int Xthal_inttype_mask[XTHAL_MAX_INTTYPES]; + +/* interrupt numbers assigned to each timer interrupt */ +extern const int Xthal_timer_interrupt[XTHAL_MAX_TIMERS]; + +/*** Virtual interrupt prioritization: ***/ + +/* Convert between interrupt levels (as per PS.INTLEVEL) and virtual interrupt priorities: */ +extern unsigned xthal_vpri_to_intlevel(unsigned vpri); +extern unsigned xthal_intlevel_to_vpri(unsigned intlevel); + +/* Enables/disables given set (mask) of interrupts; returns previous enabled-mask of all ints: */ +extern unsigned xthal_int_enable(unsigned); +extern unsigned xthal_int_disable(unsigned); + +/* Set/get virtual priority of an interrupt: */ +extern int xthal_set_int_vpri(int intnum, int vpri); +extern int xthal_get_int_vpri(int intnum); + +/* Set/get interrupt lockout level for exclusive access to virtual priority data structures: */ +extern void xthal_set_vpri_locklevel(unsigned intlevel); +extern unsigned xthal_get_vpri_locklevel(void); + +/* Set/get current virtual interrupt priority: */ +extern unsigned xthal_set_vpri(unsigned vpri); +extern unsigned xthal_get_vpri(unsigned vpri); +extern unsigned xthal_set_vpri_intlevel(unsigned intlevel); +extern unsigned xthal_set_vpri_lock(void); + + + +/*---------------------------------------------------------------------- + Generic Interrupt Trampolining Support + ----------------------------------------------------------------------*/ + +typedef void (XtHalVoidFunc)(void); + +/* + * Bitmask of interrupts currently trampolining down: + */ +extern unsigned Xthal_tram_pending; + +/* + * Bitmask of which interrupts currently trampolining down + * synchronously are actually enabled; this bitmask is necessary + * because INTENABLE cannot hold that state (sync-trampolining + * interrupts must be kept disabled while trampolining); + * in the current implementation, any bit set here is not set + * in INTENABLE, and vice-versa; once a sync-trampoline is + * handled (at level one), its enable bit must be moved from + * here to INTENABLE: + */ +extern unsigned Xthal_tram_enabled; + +/* + * Bitmask of interrupts configured for sync trampolining: + */ +extern unsigned Xthal_tram_sync; + + +/* Trampoline support functions: */ +extern unsigned xthal_tram_pending_to_service( void ); +extern void xthal_tram_done( unsigned serviced_mask ); +extern int xthal_tram_set_sync( int intnum, int sync ); +extern XtHalVoidFunc* xthal_set_tram_trigger_func( XtHalVoidFunc *trigger_fn ); + +/* INTENABLE,INTREAD,INTSET,INTCLEAR register access functions: */ +extern unsigned xthal_get_intenable( void ); +extern void xthal_set_intenable( unsigned ); +extern unsigned xthal_get_intread( void ); +extern void xthal_set_intset( unsigned ); +extern void xthal_set_intclear( unsigned ); + + +/*---------------------------------------------------------------------- + Register Windows + ----------------------------------------------------------------------*/ + +/* number of registers in register window */ +extern const unsigned int Xthal_num_aregs; +extern const unsigned char Xthal_num_aregs_log2; + +/* This spill any live register windows (other than the caller's): */ +extern void xthal_window_spill( void ); + + +/*---------------------------------------------------------------------- + Cache + ----------------------------------------------------------------------*/ + +/* size of the cache lines in log2(bytes) */ +extern const unsigned char Xthal_icache_linewidth; +extern const unsigned char Xthal_dcache_linewidth; +/* size of the cache lines in bytes */ +extern const unsigned short Xthal_icache_linesize; +extern const unsigned short Xthal_dcache_linesize; +/* number of cache sets in log2(lines per way) */ +extern const unsigned char Xthal_icache_setwidth; +extern const unsigned char Xthal_dcache_setwidth; +/* cache set associativity (number of ways) */ +extern const unsigned int Xthal_icache_ways; +extern const unsigned int Xthal_dcache_ways; +/* size of the caches in bytes (ways * 2^(linewidth + setwidth)) */ +extern const unsigned int Xthal_icache_size; +extern const unsigned int Xthal_dcache_size; +/* cache features */ +extern const unsigned char Xthal_dcache_is_writeback; +extern const unsigned char Xthal_icache_line_lockable; +extern const unsigned char Xthal_dcache_line_lockable; + +/* cache attribute register control (used by other HAL routines) */ +extern unsigned xthal_get_cacheattr( void ); +extern unsigned xthal_get_icacheattr( void ); +extern unsigned xthal_get_dcacheattr( void ); +extern void xthal_set_cacheattr( unsigned ); +extern void xthal_set_icacheattr( unsigned ); +extern void xthal_set_dcacheattr( unsigned ); + +/* initialize cache support (must be called once at startup, before all other cache calls) */ +/*extern void xthal_cache_startinit( void );*/ +/* reset caches */ +/*extern void xthal_icache_reset( void );*/ +/*extern void xthal_dcache_reset( void );*/ +/* enable caches */ +extern void xthal_icache_enable( void ); /* DEPRECATED */ +extern void xthal_dcache_enable( void ); /* DEPRECATED */ +/* disable caches */ +extern void xthal_icache_disable( void ); /* DEPRECATED */ +extern void xthal_dcache_disable( void ); /* DEPRECATED */ + +/* invalidate the caches */ +extern void xthal_icache_all_invalidate( void ); +extern void xthal_dcache_all_invalidate( void ); +extern void xthal_icache_region_invalidate( void *addr, unsigned size ); +extern void xthal_dcache_region_invalidate( void *addr, unsigned size ); +extern void xthal_icache_line_invalidate(void *addr); +extern void xthal_dcache_line_invalidate(void *addr); +/* write dirty data back */ +extern void xthal_dcache_all_writeback( void ); +extern void xthal_dcache_region_writeback( void *addr, unsigned size ); +extern void xthal_dcache_line_writeback(void *addr); +/* write dirty data back and invalidate */ +extern void xthal_dcache_all_writeback_inv( void ); +extern void xthal_dcache_region_writeback_inv( void *addr, unsigned size ); +extern void xthal_dcache_line_writeback_inv(void *addr); +/* prefetch and lock specified memory range into cache */ +extern void xthal_icache_region_lock( void *addr, unsigned size ); +extern void xthal_dcache_region_lock( void *addr, unsigned size ); +extern void xthal_icache_line_lock(void *addr); +extern void xthal_dcache_line_lock(void *addr); +/* unlock from cache */ +extern void xthal_icache_all_unlock( void ); +extern void xthal_dcache_all_unlock( void ); +extern void xthal_icache_region_unlock( void *addr, unsigned size ); +extern void xthal_dcache_region_unlock( void *addr, unsigned size ); +extern void xthal_icache_line_unlock(void *addr); +extern void xthal_dcache_line_unlock(void *addr); + + +/* sync icache and memory */ +extern void xthal_icache_sync( void ); +/* sync dcache and memory */ +extern void xthal_dcache_sync( void ); + +/*---------------------------------------------------------------------- + Debug + ----------------------------------------------------------------------*/ + +/* 1 if debug option configured, 0 if not: */ +extern const int Xthal_debug_configured; + +/* Number of instruction and data break registers: */ +extern const int Xthal_num_ibreak; +extern const int Xthal_num_dbreak; + +/* Set (plant) and remove software breakpoint, both synchronizing cache: */ +extern unsigned int xthal_set_soft_break(void *addr); +extern void xthal_remove_soft_break(void *addr, unsigned int); + + +/*---------------------------------------------------------------------- + Disassembler + ----------------------------------------------------------------------*/ + +/* Max expected size of the return buffer for a disassembled instruction (hint only): */ +#define XTHAL_DISASM_BUFSIZE 80 + +/* Disassembly option bits for selecting what to return: */ +#define XTHAL_DISASM_OPT_ADDR 0x0001 /* display address */ +#define XTHAL_DISASM_OPT_OPHEX 0x0002 /* display opcode bytes in hex */ +#define XTHAL_DISASM_OPT_OPCODE 0x0004 /* display opcode name (mnemonic) */ +#define XTHAL_DISASM_OPT_PARMS 0x0008 /* display parameters */ +#define XTHAL_DISASM_OPT_ALL 0x0FFF /* display everything */ + +/* routine to get a string for the disassembled instruction */ +extern int xthal_disassemble( unsigned char *instr_buf, void *tgt_addr, + char *buffer, unsigned buflen, unsigned options ); + +/* routine to get the size of the next instruction. Returns 0 for + illegal instruction */ +extern int xthal_disassemble_size( unsigned char *instr_buf ); + + +/*---------------------------------------------------------------------- + Core Counter + ----------------------------------------------------------------------*/ + +/* counter info */ +extern const unsigned char Xthal_have_ccount; /* set if CCOUNT register present */ +extern const unsigned char Xthal_num_ccompare; /* number of CCOMPAREn registers */ + +/* get CCOUNT register (if not present return 0) */ +extern unsigned xthal_get_ccount(void); + +/* set and get CCOMPAREn registers (if not present, get returns 0) */ +extern void xthal_set_ccompare(int, unsigned); +extern unsigned xthal_get_ccompare(int); + + +/*---------------------------------------------------------------------- + Instruction/Data RAM/ROM Access + ----------------------------------------------------------------------*/ + +extern void* xthal_memcpy(void *dst, const void *src, unsigned len); +extern void* xthal_bcopy(const void *src, void *dst, unsigned len); + +/*---------------------------------------------------------------------- + MP Synchronization + ----------------------------------------------------------------------*/ +extern int xthal_compare_and_set( int *addr, int test_val, int compare_val ); +extern unsigned xthal_get_prid( void ); + +/*extern const char Xthal_have_s32c1i;*/ +extern const unsigned char Xthal_have_prid; + + +/*---------------------------------------------------------------------- + Miscellaneous + ----------------------------------------------------------------------*/ + +extern const unsigned int Xthal_release_major; +extern const unsigned int Xthal_release_minor; +extern const char * const Xthal_release_name; +extern const char * const Xthal_release_internal; + +extern const unsigned char Xthal_memory_order; +extern const unsigned char Xthal_have_windowed; +extern const unsigned char Xthal_have_density; +extern const unsigned char Xthal_have_booleans; +extern const unsigned char Xthal_have_loops; +extern const unsigned char Xthal_have_nsa; +extern const unsigned char Xthal_have_minmax; +extern const unsigned char Xthal_have_sext; +extern const unsigned char Xthal_have_clamps; +extern const unsigned char Xthal_have_mac16; +extern const unsigned char Xthal_have_mul16; +extern const unsigned char Xthal_have_fp; +extern const unsigned char Xthal_have_speculation; +extern const unsigned char Xthal_have_exceptions; +extern const unsigned char Xthal_xea_version; +extern const unsigned char Xthal_have_interrupts; +extern const unsigned char Xthal_have_highlevel_interrupts; +extern const unsigned char Xthal_have_nmi; + +extern const unsigned short Xthal_num_writebuffer_entries; + +extern const unsigned int Xthal_build_unique_id; +/* Release info for hardware targeted by software upgrades: */ +extern const unsigned int Xthal_hw_configid0; +extern const unsigned int Xthal_hw_configid1; +extern const unsigned int Xthal_hw_release_major; +extern const unsigned int Xthal_hw_release_minor; +extern const char * const Xthal_hw_release_name; +extern const char * const Xthal_hw_release_internal; + + +/* Internal memories... */ + +extern const unsigned char Xthal_num_instrom; +extern const unsigned char Xthal_num_instram; +extern const unsigned char Xthal_num_datarom; +extern const unsigned char Xthal_num_dataram; +extern const unsigned char Xthal_num_xlmi; +extern const unsigned int Xthal_instrom_vaddr[1]; +extern const unsigned int Xthal_instrom_paddr[1]; +extern const unsigned int Xthal_instrom_size [1]; +extern const unsigned int Xthal_instram_vaddr[1]; +extern const unsigned int Xthal_instram_paddr[1]; +extern const unsigned int Xthal_instram_size [1]; +extern const unsigned int Xthal_datarom_vaddr[1]; +extern const unsigned int Xthal_datarom_paddr[1]; +extern const unsigned int Xthal_datarom_size [1]; +extern const unsigned int Xthal_dataram_vaddr[1]; +extern const unsigned int Xthal_dataram_paddr[1]; +extern const unsigned int Xthal_dataram_size [1]; +extern const unsigned int Xthal_xlmi_vaddr[1]; +extern const unsigned int Xthal_xlmi_paddr[1]; +extern const unsigned int Xthal_xlmi_size [1]; + + + +/*---------------------------------------------------------------------- + Memory Management Unit + ----------------------------------------------------------------------*/ + +extern const unsigned char Xthal_have_spanning_way; +extern const unsigned char Xthal_have_identity_map; +extern const unsigned char Xthal_have_mimic_cacheattr; +extern const unsigned char Xthal_have_xlt_cacheattr; +extern const unsigned char Xthal_have_cacheattr; +extern const unsigned char Xthal_have_tlbs; + +extern const unsigned char Xthal_mmu_asid_bits; /* 0 .. 8 */ +extern const unsigned char Xthal_mmu_asid_kernel; +extern const unsigned char Xthal_mmu_rings; /* 1 .. 4 (perhaps 0 if no MMU and/or no protection?) */ +extern const unsigned char Xthal_mmu_ring_bits; +extern const unsigned char Xthal_mmu_sr_bits; +extern const unsigned char Xthal_mmu_ca_bits; +extern const unsigned int Xthal_mmu_max_pte_page_size; +extern const unsigned int Xthal_mmu_min_pte_page_size; + +extern const unsigned char Xthal_itlb_way_bits; +extern const unsigned char Xthal_itlb_ways; +extern const unsigned char Xthal_itlb_arf_ways; +extern const unsigned char Xthal_dtlb_way_bits; +extern const unsigned char Xthal_dtlb_ways; +extern const unsigned char Xthal_dtlb_arf_ways; + +/* Convert between virtual and physical addresses (through static maps only): */ +/*** WARNING: these two functions may go away in a future release; don't depend on them! ***/ +extern int xthal_static_v2p( unsigned vaddr, unsigned *paddrp ); +extern int xthal_static_p2v( unsigned paddr, unsigned *vaddrp, unsigned cached ); + +#if 0 +/******************* EXPERIMENTAL AND TENTATIVE ONLY ********************/ + +#define XTHAL_MMU_PAGESZ_COUNT_MAX 8 /* maximum number of different page sizes */ +extern const char Xthal_mmu_pagesz_count; /* 0 .. 8 number of different page sizes configured */ + +/* Note: the following table doesn't necessarily have page sizes in increasing order: */ +extern const char Xthal_mmu_pagesz_log2[XTHAL_MMU_PAGESZ_COUNT_MAX]; /* 10 .. 28 (0 past count) */ + +/* Sorted (increasing) table of page sizes, that indexes into the above table: */ +extern const char Xthal_mmu_pagesz_sorted[XTHAL_MMU_PAGESZ_COUNT_MAX]; /* 0 .. 7 (0 past count) */ + +/*u32 Xthal_virtual_exceptions;*/ /* bitmask of which exceptions execute in virtual mode... */ + +extern const char Xthal_mmu_pte_pagesz_log2_min; /* ?? minimum page size in PTEs */ +extern const char Xthal_mmu_pte_pagesz_log2_max; /* ?? maximum page size in PTEs */ + +/* Cache Attribute Bits Implemented by the Cache (part of the cache abstraction) */ +extern const char Xthal_icache_fca_bits_implemented; /* ITLB/UTLB only! */ +extern const char Xthal_dcache_lca_bits_implemented; /* DTLB/UTLB only! */ +extern const char Xthal_dcache_sca_bits_implemented; /* DTLB/UTLB only! */ + +/* Per TLB Parameters (Instruction, Data, Unified) */ +struct XtHalMmuTlb Xthal_itlb; /* description of MMU I-TLB generic features */ +struct XtHalMmuTlb Xthal_dtlb; /* description of MMU D-TLB generic features */ +struct XtHalMmuTlb Xthal_utlb; /* description of MMU U-TLB generic features */ + +#define XTHAL_MMU_WAYS_MAX 8 /* maximum number of ways (associativities) for each TLB */ + +/* Structure for common information described for each possible TLB (instruction, data and unified): */ +typedef struct XtHalMmuTlb { + u8 va_bits; /* 32 (number of virtual address bits) */ + u8 pa_bits; /* 32 (number of physical address bits) */ + bool tlb_va_indexed; /* 1 (set if TLB is indexed by virtual address) */ + bool tlb_va_tagged; /* 0 (set if TLB is tagged by virtual address) */ + bool cache_va_indexed; /* 1 (set if cache is indexed by virtual address) */ + bool cache_va_tagged; /* 0 (set if cache is tagged by virtual address) */ + /*bool (whether page tables are traversed in vaddr sorted order, paddr sorted order, ...) */ + /*u8 (set of available page attribute bits, other than cache attribute bits defined above) */ + /*u32 (various masks for pages, MMU table/TLB entries, etc.) */ + u8 way_count; /* 0 .. 8 (number of ways, a.k.a. associativities, for this TLB) */ + XtHalMmuTlbWay * ways[XTHAL_MMU_WAYS_MAX]; /* pointers to per-way parms for each way */ +} XtHalMmuTlb; + +/* Per TLB Way (Per Associativity) Parameters */ +typedef struct XtHalMmuTlbWay { + u32 index_count_log2; /* 0 .. 4 */ + u32 pagesz_mask; /* 0 .. 2^pagesz_count - 1 (each bit corresponds to a size */ + /* defined in the Xthal_mmu_pagesz_log2[] table) */ + u32 vpn_const_mask; + u32 vpn_const_value; + u64 ppn_const_mask; /* future may support pa_bits > 32 */ + u64 ppn_const_value; + u32 ppn_id_mask; /* paddr bits taken directly from vaddr */ + bool backgnd_match; /* 0 or 1 */ + /* These are defined in terms of the XTHAL_CACHE_xxx bits: */ + u8 fca_const_mask; /* ITLB/UTLB only! */ + u8 fca_const_value; /* ITLB/UTLB only! */ + u8 lca_const_mask; /* DTLB/UTLB only! */ + u8 lca_const_value; /* DTLB/UTLB only! */ + u8 sca_const_mask; /* DTLB/UTLB only! */ + u8 sca_const_value; /* DTLB/UTLB only! */ + /* These define an encoding that map 5 bits in TLB and PTE entries to */ + /* 8 bits (FCA, ITLB), 16 bits (LCA+SCA, DTLB) or 24 bits (FCA+LCA+SCA, UTLB): */ + /* (they may be moved to struct XtHalMmuTlb) */ + u8 ca_bits; /* number of bits in TLB/PTE entries for cache attributes */ + u32 * ca_map; /* pointer to array of 2^ca_bits entries of FCA+LCA+SCA bits */ +} XtHalMmuTlbWay; + +/* + * The way to determine whether protection support is present in core + * is to [look at Xthal_mmu_rings ???]. + * Give info on memory requirements for MMU tables and other in-memory + * data structures (globally, per task, base and per page, etc.) - whatever bounds can be calculated. + */ + + +/* Default vectors: */ +xthal_immu_fetch_miss_vector +xthal_dmmu_load_miss_vector +xthal_dmmu_store_miss_vector + +/* Functions called when a fault is detected: */ +typedef void (XtHalMmuFaultFunc)( unsigned vaddr, ...context... ); +/* Or, */ +/* a? = vaddr */ +/* a? = context... */ +/* PS.xxx = xxx */ +XtHalMMuFaultFunc *Xthal_immu_fetch_fault_func; +XtHalMMuFaultFunc *Xthal_dmmu_load_fault_func; +XtHalMMuFaultFunc *Xthal_dmmu_store_fault_func; + +/* Default Handlers: */ +/* The user and/or kernel exception handlers may jump to these handlers to handle the relevant exceptions, + * according to the value of EXCCAUSE. The exact register state on entry to these handlers is TBD. */ +/* When multiple TLB entries match (hit) on the same access: */ +xthal_immu_fetch_multihit_handler +xthal_dmmu_load_multihit_handler +xthal_dmmu_store_multihit_handler +/* Protection violations according to cache attributes, and other cache attribute mismatches: */ +xthal_immu_fetch_attr_handler +xthal_dmmu_load_attr_handler +xthal_dmmu_store_attr_handler +/* Protection violations due to insufficient ring level: */ +xthal_immu_fetch_priv_handler +xthal_dmmu_load_priv_handler +xthal_dmmu_store_priv_handler +/* Alignment exception handlers (if supported by the particular Xtensa MMU configuration): */ +xthal_dmmu_load_align_handler +xthal_dmmu_store_align_handler + +/* Or, alternatively, the OS user and/or kernel exception handlers may simply jump to the + * following entry points which will handle any values of EXCCAUSE not handled by the OS: */ +xthal_user_exc_default_handler +xthal_kernel_exc_default_handler + +#endif /*0*/ + +#ifdef INCLUDE_DEPRECATED_HAL_CODE +extern const unsigned char Xthal_have_old_exc_arch; +extern const unsigned char Xthal_have_mmu; +extern const unsigned int Xthal_num_regs; +extern const unsigned char Xthal_num_iroms; +extern const unsigned char Xthal_num_irams; +extern const unsigned char Xthal_num_droms; +extern const unsigned char Xthal_num_drams; +extern const unsigned int Xthal_configid0; +extern const unsigned int Xthal_configid1; +#endif + +#ifdef INCLUDE_DEPRECATED_HAL_DEBUG_CODE +#define XTHAL_24_BIT_BREAK 0x80000000 +#define XTHAL_16_BIT_BREAK 0x40000000 +extern const unsigned short Xthal_ill_inst_16[16]; +#define XTHAL_DEST_REG 0xf0000000 /* Mask for destination register */ +#define XTHAL_DEST_REG_INST 0x08000000 /* Branch address is in register */ +#define XTHAL_DEST_REL_INST 0x04000000 /* Branch address is relative */ +#define XTHAL_RFW_INST 0x00000800 +#define XTHAL_RFUE_INST 0x00000400 +#define XTHAL_RFI_INST 0x00000200 +#define XTHAL_RFE_INST 0x00000100 +#define XTHAL_RET_INST 0x00000080 +#define XTHAL_BREAK_INST 0x00000040 +#define XTHAL_SYSCALL_INST 0x00000020 +#define XTHAL_LOOP_END 0x00000010 /* Not set by xthal_inst_type */ +#define XTHAL_JUMP_INST 0x00000008 /* Call or jump instruction */ +#define XTHAL_BRANCH_INST 0x00000004 /* Branch instruction */ +#define XTHAL_24_BIT_INST 0x00000002 +#define XTHAL_16_BIT_INST 0x00000001 +typedef struct xthal_state { + unsigned pc; + unsigned ar[16]; + unsigned lbeg; + unsigned lend; + unsigned lcount; + unsigned extra_ptr; + unsigned cpregs_ptr[XTHAL_MAX_CPS]; +} XTHAL_STATE; +extern unsigned int xthal_inst_type(void *addr); +extern unsigned int xthal_branch_addr(void *addr); +extern unsigned int xthal_get_npc(XTHAL_STATE *user_state); +#endif /* INCLUDE_DEPRECATED_HAL_DEBUG_CODE */ + +#ifdef __cplusplus +} +#endif +#endif /*!__ASSEMBLY__ */ + +#endif /*XTENSA_HAL_H*/ + diff --git a/include/asm-xtensa/xtensa/simcall.h b/include/asm-xtensa/xtensa/simcall.h new file mode 100644 index 000000000000..a2b868929a49 --- /dev/null +++ b/include/asm-xtensa/xtensa/simcall.h @@ -0,0 +1,130 @@ +#ifndef SIMCALL_INCLUDED +#define SIMCALL_INCLUDED + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * include/asm-xtensa/xtensa/simcall.h - Simulator call numbers + * + * 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) 2002 Tensilica Inc. + */ + + +/* + * System call like services offered by the simulator host. + * These are modeled after the Linux 2.4 kernel system calls + * for Xtensa processors. However not all system calls and + * not all functionality of a given system call are implemented, + * or necessarily have well defined or equivalent semantics in + * the context of a simulation (as opposed to a Unix kernel). + * + * These services behave largely as if they had been invoked + * as a task in the simulator host's operating system + * (eg. files accessed are those of the simulator host). + * However, these SIMCALLs model a virtual operating system + * so that various definitions, bit assignments etc + * (eg. open mode bits, errno values, etc) are independent + * of the host operating system used to run the simulation. + * Rather these definitions are specific to the Xtensa ISS. + * This way Xtensa ISA code written to use these SIMCALLs + * can (in principle) be simulated on any host. + * + * Up to 6 parameters are passed in registers a3 to a8 + * (note the 6th parameter isn't passed on the stack, + * unlike windowed function calling conventions). + * The return value is in a2. A negative value in the + * range -4096 to -1 indicates a negated error code to be + * reported in errno with a return value of -1, otherwise + * the value in a2 is returned as is. + */ + +/* These #defines need to match what's in Xtensa/OS/vxworks/xtiss/simcalls.c */ + +#define SYS_nop 0 /* n/a - setup; used to flush register windows */ +#define SYS_exit 1 /*x*/ +#define SYS_fork 2 +#define SYS_read 3 /*x*/ +#define SYS_write 4 /*x*/ +#define SYS_open 5 /*x*/ +#define SYS_close 6 /*x*/ +#define SYS_rename 7 /*x 38 - waitpid */ +#define SYS_creat 8 /*x*/ +#define SYS_link 9 /*x (not implemented on WIN32) */ +#define SYS_unlink 10 /*x*/ +#define SYS_execv 11 /* n/a - execve */ +#define SYS_execve 12 /* 11 - chdir */ +#define SYS_pipe 13 /* 42 - time */ +#define SYS_stat 14 /* 106 - mknod */ +#define SYS_chmod 15 +#define SYS_chown 16 /* 202 - lchown */ +#define SYS_utime 17 /* 30 - break */ +#define SYS_wait 18 /* n/a - oldstat */ +#define SYS_lseek 19 /*x*/ +#define SYS_getpid 20 +#define SYS_isatty 21 /* n/a - mount */ +#define SYS_fstat 22 /* 108 - oldumount */ +#define SYS_time 23 /* 13 - setuid */ +#define SYS_gettimeofday 24 /*x 78 - getuid (not implemented on WIN32) */ +#define SYS_times 25 /*X 43 - stime (Xtensa-specific implementation) */ +#define SYS_socket 26 +#define SYS_sendto 27 +#define SYS_recvfrom 28 +#define SYS_select_one 29 /* not compitible select, one file descriptor at the time */ +#define SYS_bind 30 +#define SYS_ioctl 31 + +/* + * Other... + */ +#define SYS_iss_argc 1000 /* returns value of argc */ +#define SYS_iss_argv_size 1001 /* bytes needed for argv & arg strings */ +#define SYS_iss_set_argv 1002 /* saves argv & arg strings at given addr */ + +/* + * SIMCALLs for the ferret memory debugger. All are invoked by + * libferret.a ... ( Xtensa/Target-Libs/ferret ) + */ +#define SYS_ferret 1010 +#define SYS_malloc 1011 +#define SYS_free 1012 +#define SYS_more_heap 1013 +#define SYS_no_heap 1014 + + +/* + * Extra SIMCALLs for GDB: + */ +#define SYS_gdb_break -1 /* invoked by XTOS on user exceptions if EPC points + to a break.n/break, regardless of cause! */ +#define SYS_xmon_out -2 /* invoked by XMON: ... */ +#define SYS_xmon_in -3 /* invoked by XMON: ... */ +#define SYS_xmon_flush -4 /* invoked by XMON: ... */ +#define SYS_gdb_abort -5 /* invoked by XTOS in _xtos_panic() */ +#define SYS_gdb_illegal_inst -6 /* invoked by XTOS for illegal instructions (too deeply) */ +#define SYS_xmon_init -7 /* invoked by XMON: ... */ +#define SYS_gdb_enter_sktloop -8 /* invoked by XTOS on debug exceptions */ + +/* + * SIMCALLs for vxWorks xtiss BSP: + */ +#define SYS_setup_ppp_pipes -83 +#define SYS_log_msg -84 + +/* + * Test SIMCALLs: + */ +#define SYS_test_write_state -100 +#define SYS_test_read_state -101 + +/* + * SYS_select_one specifiers + */ +#define XTISS_SELECT_ONE_READ 1 +#define XTISS_SELECT_ONE_WRITE 2 +#define XTISS_SELECT_ONE_EXCEPT 3 + +#endif /* !SIMCALL_INCLUDED */ diff --git a/include/asm-xtensa/xtensa/xt2000-uart.h b/include/asm-xtensa/xtensa/xt2000-uart.h new file mode 100644 index 000000000000..0154460f0ed8 --- /dev/null +++ b/include/asm-xtensa/xtensa/xt2000-uart.h @@ -0,0 +1,155 @@ +#ifndef _uart_h_included_ +#define _uart_h_included_ + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * include/asm-xtensa/xtensa/xt2000-uart.h -- NatSemi PC16552D DUART + * definitions + * + * 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) 2002 Tensilica Inc. + */ + + +#include + + +/* 16550 UART DEVICE REGISTERS + The XT2000 board aligns each register to a 32-bit word but the UART device only uses + one byte of the word, which is the least-significant byte regardless of the + endianness of the core (ie. byte offset 0 for little-endian and 3 for big-endian). + So if using word accesses then endianness doesn't matter. + The macros provided here do that. +*/ +struct uart_dev_s { + union { + unsigned int rxb; /* DLAB=0: receive buffer, read-only */ + unsigned int txb; /* DLAB=0: transmit buffer, write-only */ + unsigned int dll; /* DLAB=1: divisor, least-significant byte latch (was write-only?) */ + } w0; + union { + unsigned int ier; /* DLAB=0: interrupt-enable register (was write-only?) */ + unsigned int dlm; /* DLAB=1: divisor, most-significant byte latch (was write-only?) */ + } w1; + + union { + unsigned int isr; /* DLAB=0: interrupt status register, read-only */ + unsigned int fcr; /* DLAB=0: FIFO control register, write-only */ + unsigned int afr; /* DLAB=1: alternate function register */ + } w2; + + unsigned int lcr; /* line control-register, write-only */ + unsigned int mcr; /* modem control-regsiter, write-only */ + unsigned int lsr; /* line status register, read-only */ + unsigned int msr; /* modem status register, read-only */ + unsigned int scr; /* scratch regsiter, read/write */ +}; + +#define _RXB(u) ((u)->w0.rxb) +#define _TXB(u) ((u)->w0.txb) +#define _DLL(u) ((u)->w0.dll) +#define _IER(u) ((u)->w1.ier) +#define _DLM(u) ((u)->w1.dlm) +#define _ISR(u) ((u)->w2.isr) +#define _FCR(u) ((u)->w2.fcr) +#define _AFR(u) ((u)->w2.afr) +#define _LCR(u) ((u)->lcr) +#define _MCR(u) ((u)->mcr) +#define _LSR(u) ((u)->lsr) +#define _MSR(u) ((u)->msr) +#define _SCR(u) ((u)->scr) + +typedef volatile struct uart_dev_s uart_dev_t; + +/* IER bits */ +#define RCVR_DATA_REG_INTENABLE 0x01 +#define XMIT_HOLD_REG_INTENABLE 0x02 +#define RCVR_STATUS_INTENABLE 0x04 +#define MODEM_STATUS_INTENABLE 0x08 + +/* FCR bits */ +#define _FIFO_ENABLE 0x01 +#define RCVR_FIFO_RESET 0x02 +#define XMIT_FIFO_RESET 0x04 +#define DMA_MODE_SELECT 0x08 +#define RCVR_TRIGGER_LSB 0x40 +#define RCVR_TRIGGER_MSB 0x80 + +/* AFR bits */ +#define AFR_CONC_WRITE 0x01 +#define AFR_BAUDOUT_SEL 0x02 +#define AFR_RXRDY_SEL 0x04 + +/* ISR bits */ +#define INT_STATUS(r) ((r)&1) +#define INT_PRIORITY(r) (((r)>>1)&0x7) + +/* LCR bits */ +#define WORD_LENGTH(n) (((n)-5)&0x3) +#define STOP_BIT_ENABLE 0x04 +#define PARITY_ENABLE 0x08 +#define EVEN_PARITY 0x10 +#define FORCE_PARITY 0x20 +#define XMIT_BREAK 0x40 +#define DLAB_ENABLE 0x80 + +/* MCR bits */ +#define _DTR 0x01 +#define _RTS 0x02 +#define _OP1 0x04 +#define _OP2 0x08 +#define LOOP_BACK 0x10 + +/* LSR Bits */ +#define RCVR_DATA_READY 0x01 +#define OVERRUN_ERROR 0x02 +#define PARITY_ERROR 0x04 +#define FRAMING_ERROR 0x08 +#define BREAK_INTERRUPT 0x10 +#define XMIT_HOLD_EMPTY 0x20 +#define XMIT_EMPTY 0x40 +#define FIFO_ERROR 0x80 +#define RCVR_READY(u) (_LSR(u)&RCVR_DATA_READY) +#define XMIT_READY(u) (_LSR(u)&XMIT_HOLD_EMPTY) + +/* MSR bits */ +#define _RDR 0x01 +#define DELTA_DSR 0x02 +#define DELTA_RI 0x04 +#define DELTA_CD 0x08 +#define _CTS 0x10 +#define _DSR 0x20 +#define _RI 0x40 +#define _CD 0x80 + +/* prototypes */ +void uart_init( uart_dev_t *u, int bitrate ); +void uart_out( uart_dev_t *u, char c ); +void uart_puts( uart_dev_t *u, char *s ); +char uart_in( uart_dev_t *u ); +void uart_enable_rcvr_int( uart_dev_t *u ); +void uart_disable_rcvr_int( uart_dev_t *u ); + +#ifdef DUART16552_1_VADDR +/* DUART present. */ +#define DUART_1_BASE (*(uart_dev_t*)DUART16552_1_VADDR) +#define DUART_2_BASE (*(uart_dev_t*)DUART16552_2_VADDR) +#define UART1_PUTS(s) uart_puts( &DUART_1_BASE, s ) +#define UART2_PUTS(s) uart_puts( &DUART_2_BASE, s ) +#else +/* DUART not configured, use dummy placeholders to allow compiles to work. */ +#define DUART_1_BASE (*(uart_dev_t*)0) +#define DUART_2_BASE (*(uart_dev_t*)0) +#define UART1_PUTS(s) +#define UART2_PUTS(s) +#endif + +/* Compute 16-bit divisor for baudrate generator, with rounding: */ +#define DUART_DIVISOR(crystal,speed) (((crystal)/16 + (speed)/2)/(speed)) + +#endif /*_uart_h_included_*/ + diff --git a/include/asm-xtensa/xtensa/xt2000.h b/include/asm-xtensa/xtensa/xt2000.h new file mode 100644 index 000000000000..703a45002f8f --- /dev/null +++ b/include/asm-xtensa/xtensa/xt2000.h @@ -0,0 +1,408 @@ +#ifndef _INC_XT2000_H_ +#define _INC_XT2000_H_ + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * include/asm-xtensa/xtensa/xt2000.h - Definitions specific to the + * Tensilica XT2000 Emulation Board + * + * 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) 2002 Tensilica Inc. + */ + + +#include +#include + + +/* + * Default assignment of XT2000 devices to external interrupts. + */ + +/* Ethernet interrupt: */ +#ifdef XCHAL_EXTINT3_NUM +#define SONIC83934_INTNUM XCHAL_EXTINT3_NUM +#define SONIC83934_INTLEVEL XCHAL_EXTINT3_LEVEL +#define SONIC83934_INTMASK XCHAL_EXTINT3_MASK +#else +#define SONIC83934_INTMASK 0 +#endif + +/* DUART channel 1 interrupt (P1 - console): */ +#ifdef XCHAL_EXTINT4_NUM +#define DUART16552_1_INTNUM XCHAL_EXTINT4_NUM +#define DUART16552_1_INTLEVEL XCHAL_EXTINT4_LEVEL +#define DUART16552_1_INTMASK XCHAL_EXTINT4_MASK +#else +#define DUART16552_1_INTMASK 0 +#endif + +/* DUART channel 2 interrupt (P2 - 2nd serial port): */ +#ifdef XCHAL_EXTINT5_NUM +#define DUART16552_2_INTNUM XCHAL_EXTINT5_NUM +#define DUART16552_2_INTLEVEL XCHAL_EXTINT5_LEVEL +#define DUART16552_2_INTMASK XCHAL_EXTINT5_MASK +#else +#define DUART16552_2_INTMASK 0 +#endif + +/* FPGA-combined PCI/etc interrupts: */ +#ifdef XCHAL_EXTINT6_NUM +#define XT2000_FPGAPCI_INTNUM XCHAL_EXTINT6_NUM +#define XT2000_FPGAPCI_INTLEVEL XCHAL_EXTINT6_LEVEL +#define XT2000_FPGAPCI_INTMASK XCHAL_EXTINT6_MASK +#else +#define XT2000_FPGAPCI_INTMASK 0 +#endif + + + +/* + * Device addresses. + * + * Note: for endianness-independence, use 32-bit loads and stores for all + * register accesses to Ethernet, DUART and LED devices. Undefined bits + * may need to be masked out if needed when reading if the actual register + * size is smaller than 32 bits. + * + * Note: XT2000 bus byte lanes are defined in terms of msbyte and lsbyte + * relative to the processor. So 32-bit registers are accessed consistently + * from both big and little endian processors. However, this means byte + * sequences are not consistent between big and little endian processors. + * This is fine for RAM, and for ROM if ROM is created for a specific + * processor (and thus has correct byte sequences). However this may be + * unexpected for Flash, which might contain a file-system that one wants + * to use for multiple processor configurations (eg. the Flash might contain + * the Ethernet card's address, endianness-independent application data, etc). + * That is, byte sequences written in Flash by a core of a given endianness + * will be byte-swapped when seen by a core of the other endianness. + * Someone implementing an endianness-independent Flash file system will + * likely handle this byte-swapping issue in the Flash driver software. + */ + +#define DUART16552_XTAL_FREQ 18432000 /* crystal frequency in Hz */ +#define XTBOARD_FLASH_MAXSIZE 0x4000000 /* 64 MB (max; depends on what is socketed!) */ +#define XTBOARD_EPROM_MAXSIZE 0x0400000 /* 4 MB (max; depends on what is socketed!) */ +#define XTBOARD_EEPROM_MAXSIZE 0x0080000 /* 512 kB (max; depends on what is socketed!) */ +#define XTBOARD_ASRAM_SIZE 0x0100000 /* 1 MB */ +#define XTBOARD_PCI_MEM_SIZE 0x8000000 /* 128 MB (allocated) */ +#define XTBOARD_PCI_IO_SIZE 0x1000000 /* 16 MB (allocated) */ + +#ifdef XSHAL_IOBLOCK_BYPASS_PADDR +/* PCI memory space: */ +# define XTBOARD_PCI_MEM_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0x0000000) +/* Socketed Flash (eg. 2 x 16-bit devices): */ +# define XTBOARD_FLASH_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0x8000000) +/* PCI I/O space: */ +# define XTBOARD_PCI_IO_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xC000000) +/* V3 PCI interface chip register/config space: */ +# define XTBOARD_V3PCI_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD000000) +/* Bus Interface registers: */ +# define XTBOARD_BUSINT_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD010000) +/* FPGA registers: */ +# define XT2000_FPGAREGS_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD020000) +/* SONIC SN83934 Ethernet controller/transceiver: */ +# define SONIC83934_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD030000) +/* 8-character bitmapped LED display: */ +# define XTBOARD_LED_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD040000) +/* National-Semi PC16552D DUART: */ +# define DUART16552_1_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD050020) /* channel 1 (P1 - console) */ +# define DUART16552_2_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD050000) /* channel 2 (P2) */ +/* Asynchronous Static RAM: */ +# define XTBOARD_ASRAM_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD400000) +/* 8-bit EEPROM: */ +# define XTBOARD_EEPROM_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD600000) +/* 2 x 16-bit EPROMs: */ +# define XTBOARD_EPROM_PADDR (XSHAL_IOBLOCK_BYPASS_PADDR+0xD800000) +#endif /* XSHAL_IOBLOCK_BYPASS_PADDR */ + +/* These devices might be accessed cached: */ +#ifdef XSHAL_IOBLOCK_CACHED_PADDR +# define XTBOARD_PCI_MEM_CACHED_PADDR (XSHAL_IOBLOCK_CACHED_PADDR+0x0000000) +# define XTBOARD_FLASH_CACHED_PADDR (XSHAL_IOBLOCK_CACHED_PADDR+0x8000000) +# define XTBOARD_ASRAM_CACHED_PADDR (XSHAL_IOBLOCK_CACHED_PADDR+0xD400000) +# define XTBOARD_EEPROM_CACHED_PADDR (XSHAL_IOBLOCK_CACHED_PADDR+0xD600000) +# define XTBOARD_EPROM_CACHED_PADDR (XSHAL_IOBLOCK_CACHED_PADDR+0xD800000) +#endif /* XSHAL_IOBLOCK_CACHED_PADDR */ + + +/*** Same thing over again, this time with virtual addresses: ***/ + +#ifdef XSHAL_IOBLOCK_BYPASS_VADDR +/* PCI memory space: */ +# define XTBOARD_PCI_MEM_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0x0000000) +/* Socketed Flash (eg. 2 x 16-bit devices): */ +# define XTBOARD_FLASH_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0x8000000) +/* PCI I/O space: */ +# define XTBOARD_PCI_IO_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xC000000) +/* V3 PCI interface chip register/config space: */ +# define XTBOARD_V3PCI_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD000000) +/* Bus Interface registers: */ +# define XTBOARD_BUSINT_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD010000) +/* FPGA registers: */ +# define XT2000_FPGAREGS_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD020000) +/* SONIC SN83934 Ethernet controller/transceiver: */ +# define SONIC83934_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD030000) +/* 8-character bitmapped LED display: */ +# define XTBOARD_LED_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD040000) +/* National-Semi PC16552D DUART: */ +# define DUART16552_1_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD050020) /* channel 1 (P1 - console) */ +# define DUART16552_2_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD050000) /* channel 2 (P2) */ +/* Asynchronous Static RAM: */ +# define XTBOARD_ASRAM_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD400000) +/* 8-bit EEPROM: */ +# define XTBOARD_EEPROM_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD600000) +/* 2 x 16-bit EPROMs: */ +# define XTBOARD_EPROM_VADDR (XSHAL_IOBLOCK_BYPASS_VADDR+0xD800000) +#endif /* XSHAL_IOBLOCK_BYPASS_VADDR */ + +/* These devices might be accessed cached: */ +#ifdef XSHAL_IOBLOCK_CACHED_VADDR +# define XTBOARD_PCI_MEM_CACHED_VADDR (XSHAL_IOBLOCK_CACHED_VADDR+0x0000000) +# define XTBOARD_FLASH_CACHED_VADDR (XSHAL_IOBLOCK_CACHED_VADDR+0x8000000) +# define XTBOARD_ASRAM_CACHED_VADDR (XSHAL_IOBLOCK_CACHED_VADDR+0xD400000) +# define XTBOARD_EEPROM_CACHED_VADDR (XSHAL_IOBLOCK_CACHED_VADDR+0xD600000) +# define XTBOARD_EPROM_CACHED_VADDR (XSHAL_IOBLOCK_CACHED_VADDR+0xD800000) +#endif /* XSHAL_IOBLOCK_CACHED_VADDR */ + + +/* System ROM: */ +#define XTBOARD_ROM_SIZE XSHAL_ROM_SIZE +#ifdef XSHAL_ROM_VADDR +#define XTBOARD_ROM_VADDR XSHAL_ROM_VADDR +#endif +#ifdef XSHAL_ROM_PADDR +#define XTBOARD_ROM_PADDR XSHAL_ROM_PADDR +#endif + +/* System RAM: */ +#define XTBOARD_RAM_SIZE XSHAL_RAM_SIZE +#ifdef XSHAL_RAM_VADDR +#define XTBOARD_RAM_VADDR XSHAL_RAM_VADDR +#endif +#ifdef XSHAL_RAM_PADDR +#define XTBOARD_RAM_PADDR XSHAL_RAM_PADDR +#endif +#define XTBOARD_RAM_BYPASS_VADDR XSHAL_RAM_BYPASS_VADDR +#define XTBOARD_RAM_BYPASS_PADDR XSHAL_RAM_BYPASS_PADDR + + + +/* + * Things that depend on device addresses. + */ + + +#define XTBOARD_CACHEATTR_WRITEBACK XSHAL_XT2000_CACHEATTR_WRITEBACK +#define XTBOARD_CACHEATTR_WRITEALLOC XSHAL_XT2000_CACHEATTR_WRITEALLOC +#define XTBOARD_CACHEATTR_WRITETHRU XSHAL_XT2000_CACHEATTR_WRITETHRU +#define XTBOARD_CACHEATTR_BYPASS XSHAL_XT2000_CACHEATTR_BYPASS +#define XTBOARD_CACHEATTR_DEFAULT XSHAL_XT2000_CACHEATTR_DEFAULT + +#define XTBOARD_BUSINT_PIPE_REGIONS XSHAL_XT2000_PIPE_REGIONS +#define XTBOARD_BUSINT_SDRAM_REGIONS XSHAL_XT2000_SDRAM_REGIONS + + + +/* + * BusLogic (FPGA) registers. + * All these registers are normally accessed using 32-bit loads/stores. + */ + +/* Register offsets: */ +#define XT2000_DATECD_OFS 0x00 /* date code (read-only) */ +#define XT2000_STSREG_OFS 0x04 /* status (read-only) */ +#define XT2000_SYSLED_OFS 0x08 /* system LED */ +#define XT2000_WRPROT_OFS 0x0C /* write protect */ +#define XT2000_SWRST_OFS 0x10 /* software reset */ +#define XT2000_SYSRST_OFS 0x14 /* system (peripherals) reset */ +#define XT2000_IMASK_OFS 0x18 /* interrupt mask */ +#define XT2000_ISTAT_OFS 0x1C /* interrupt status */ +#define XT2000_V3CFG_OFS 0x20 /* V3 config (V320 PCI) */ + +/* Physical register addresses: */ +#ifdef XT2000_FPGAREGS_PADDR +#define XT2000_DATECD_PADDR (XT2000_FPGAREGS_PADDR+XT2000_DATECD_OFS) +#define XT2000_STSREG_PADDR (XT2000_FPGAREGS_PADDR+XT2000_STSREG_OFS) +#define XT2000_SYSLED_PADDR (XT2000_FPGAREGS_PADDR+XT2000_SYSLED_OFS) +#define XT2000_WRPROT_PADDR (XT2000_FPGAREGS_PADDR+XT2000_WRPROT_OFS) +#define XT2000_SWRST_PADDR (XT2000_FPGAREGS_PADDR+XT2000_SWRST_OFS) +#define XT2000_SYSRST_PADDR (XT2000_FPGAREGS_PADDR+XT2000_SYSRST_OFS) +#define XT2000_IMASK_PADDR (XT2000_FPGAREGS_PADDR+XT2000_IMASK_OFS) +#define XT2000_ISTAT_PADDR (XT2000_FPGAREGS_PADDR+XT2000_ISTAT_OFS) +#define XT2000_V3CFG_PADDR (XT2000_FPGAREGS_PADDR+XT2000_V3CFG_OFS) +#endif + +/* Virtual register addresses: */ +#ifdef XT2000_FPGAREGS_VADDR +#define XT2000_DATECD_VADDR (XT2000_FPGAREGS_VADDR+XT2000_DATECD_OFS) +#define XT2000_STSREG_VADDR (XT2000_FPGAREGS_VADDR+XT2000_STSREG_OFS) +#define XT2000_SYSLED_VADDR (XT2000_FPGAREGS_VADDR+XT2000_SYSLED_OFS) +#define XT2000_WRPROT_VADDR (XT2000_FPGAREGS_VADDR+XT2000_WRPROT_OFS) +#define XT2000_SWRST_VADDR (XT2000_FPGAREGS_VADDR+XT2000_SWRST_OFS) +#define XT2000_SYSRST_VADDR (XT2000_FPGAREGS_VADDR+XT2000_SYSRST_OFS) +#define XT2000_IMASK_VADDR (XT2000_FPGAREGS_VADDR+XT2000_IMASK_OFS) +#define XT2000_ISTAT_VADDR (XT2000_FPGAREGS_VADDR+XT2000_ISTAT_OFS) +#define XT2000_V3CFG_VADDR (XT2000_FPGAREGS_VADDR+XT2000_V3CFG_OFS) +/* Register access (for C code): */ +#define XT2000_DATECD_REG (*(volatile unsigned*) XT2000_DATECD_VADDR) +#define XT2000_STSREG_REG (*(volatile unsigned*) XT2000_STSREG_VADDR) +#define XT2000_SYSLED_REG (*(volatile unsigned*) XT2000_SYSLED_VADDR) +#define XT2000_WRPROT_REG (*(volatile unsigned*) XT2000_WRPROT_VADDR) +#define XT2000_SWRST_REG (*(volatile unsigned*) XT2000_SWRST_VADDR) +#define XT2000_SYSRST_REG (*(volatile unsigned*) XT2000_SYSRST_VADDR) +#define XT2000_IMASK_REG (*(volatile unsigned*) XT2000_IMASK_VADDR) +#define XT2000_ISTAT_REG (*(volatile unsigned*) XT2000_ISTAT_VADDR) +#define XT2000_V3CFG_REG (*(volatile unsigned*) XT2000_V3CFG_VADDR) +#endif + +/* DATECD (date code) bit fields: */ + +/* BCD-coded month (01..12): */ +#define XT2000_DATECD_MONTH_SHIFT 24 +#define XT2000_DATECD_MONTH_BITS 8 +#define XT2000_DATECD_MONTH_MASK 0xFF000000 +/* BCD-coded day (01..31): */ +#define XT2000_DATECD_DAY_SHIFT 16 +#define XT2000_DATECD_DAY_BITS 8 +#define XT2000_DATECD_DAY_MASK 0x00FF0000 +/* BCD-coded year (2001..9999): */ +#define XT2000_DATECD_YEAR_SHIFT 0 +#define XT2000_DATECD_YEAR_BITS 16 +#define XT2000_DATECD_YEAR_MASK 0x0000FFFF + +/* STSREG (status) bit fields: */ + +/* Switch SW3 setting bit fields (0=off/up, 1=on/down): */ +#define XT2000_STSREG_SW3_SHIFT 0 +#define XT2000_STSREG_SW3_BITS 4 +#define XT2000_STSREG_SW3_MASK 0x0000000F +/* Boot-select bits of switch SW3: */ +#define XT2000_STSREG_BOOTSEL_SHIFT 0 +#define XT2000_STSREG_BOOTSEL_BITS 2 +#define XT2000_STSREG_BOOTSEL_MASK 0x00000003 +/* Boot-select values: */ +#define XT2000_STSREG_BOOTSEL_FLASH 0 +#define XT2000_STSREG_BOOTSEL_EPROM16 1 +#define XT2000_STSREG_BOOTSEL_PROM8 2 +#define XT2000_STSREG_BOOTSEL_ASRAM 3 +/* User-defined bits of switch SW3: */ +#define XT2000_STSREG_SW3_2_SHIFT 2 +#define XT2000_STSREG_SW3_2_MASK 0x00000004 +#define XT2000_STSREG_SW3_3_SHIFT 3 +#define XT2000_STSREG_SW3_3_MASK 0x00000008 + +/* SYSLED (system LED) bit fields: */ + +/* LED control bit (0=off, 1=on): */ +#define XT2000_SYSLED_LEDON_SHIFT 0 +#define XT2000_SYSLED_LEDON_MASK 0x00000001 + +/* WRPROT (write protect) bit fields (0=writable, 1=write-protected [default]): */ + +/* Flash write protect: */ +#define XT2000_WRPROT_FLWP_SHIFT 0 +#define XT2000_WRPROT_FLWP_MASK 0x00000001 +/* Reserved but present write protect bits: */ +#define XT2000_WRPROT_WRP_SHIFT 1 +#define XT2000_WRPROT_WRP_BITS 7 +#define XT2000_WRPROT_WRP_MASK 0x000000FE + +/* SWRST (software reset; allows s/w to generate power-on equivalent reset): */ + +/* Software reset bits: */ +#define XT2000_SWRST_SWR_SHIFT 0 +#define XT2000_SWRST_SWR_BITS 16 +#define XT2000_SWRST_SWR_MASK 0x0000FFFF +/* Software reset value -- writing this value resets the board: */ +#define XT2000_SWRST_RESETVALUE 0x0000DEAD + +/* SYSRST (system reset; controls reset of individual peripherals): */ + +/* All-device reset: */ +#define XT2000_SYSRST_ALL_SHIFT 0 +#define XT2000_SYSRST_ALL_BITS 4 +#define XT2000_SYSRST_ALL_MASK 0x0000000F +/* HDSP-2534 LED display reset (1=reset, 0=nothing): */ +#define XT2000_SYSRST_LED_SHIFT 0 +#define XT2000_SYSRST_LED_MASK 0x00000001 +/* Sonic DP83934 Ethernet controller reset (1=reset, 0=nothing): */ +#define XT2000_SYSRST_SONIC_SHIFT 1 +#define XT2000_SYSRST_SONIC_MASK 0x00000002 +/* DP16552 DUART reset (1=reset, 0=nothing): */ +#define XT2000_SYSRST_DUART_SHIFT 2 +#define XT2000_SYSRST_DUART_MASK 0x00000004 +/* V3 V320 PCI bridge controller reset (1=reset, 0=nothing): */ +#define XT2000_SYSRST_V3_SHIFT 3 +#define XT2000_SYSRST_V3_MASK 0x00000008 + +/* IMASK (interrupt mask; 0=disable, 1=enable): */ +/* ISTAT (interrupt status; 0=inactive, 1=pending): */ + +/* PCI INTP interrupt: */ +#define XT2000_INTMUX_PCI_INTP_SHIFT 2 +#define XT2000_INTMUX_PCI_INTP_MASK 0x00000004 +/* PCI INTS interrupt: */ +#define XT2000_INTMUX_PCI_INTS_SHIFT 3 +#define XT2000_INTMUX_PCI_INTS_MASK 0x00000008 +/* PCI INTD interrupt: */ +#define XT2000_INTMUX_PCI_INTD_SHIFT 4 +#define XT2000_INTMUX_PCI_INTD_MASK 0x00000010 +/* V320 PCI controller interrupt: */ +#define XT2000_INTMUX_V3_SHIFT 5 +#define XT2000_INTMUX_V3_MASK 0x00000020 +/* PCI ENUM interrupt: */ +#define XT2000_INTMUX_PCI_ENUM_SHIFT 6 +#define XT2000_INTMUX_PCI_ENUM_MASK 0x00000040 +/* PCI DEG interrupt: */ +#define XT2000_INTMUX_PCI_DEG_SHIFT 7 +#define XT2000_INTMUX_PCI_DEG_MASK 0x00000080 + +/* V3CFG (V3 config, V320 PCI controller): */ + +/* V3 address control (0=pass-thru, 1=V3 address bits 31:28 set to 4'b0001 [default]): */ +#define XT2000_V3CFG_V3ADC_SHIFT 0 +#define XT2000_V3CFG_V3ADC_MASK 0x00000001 + +/* I2C Devices */ + +#define XT2000_I2C_RTC_ID 0x68 +#define XT2000_I2C_NVRAM0_ID 0x56 /* 1st 256 byte block */ +#define XT2000_I2C_NVRAM1_ID 0x57 /* 2nd 256 byte block */ + +/* NVRAM Board Info structure: */ + +#define XT2000_NVRAM_SIZE 512 + +#define XT2000_NVRAM_BINFO_START 0x100 +#define XT2000_NVRAM_BINFO_SIZE 0x20 +#define XT2000_NVRAM_BINFO_VERSION 0x10 /* version 1.0 */ +#if 0 +#define XT2000_NVRAM_BINFO_VERSION_OFFSET 0x00 +#define XT2000_NVRAM_BINFO_VERSION_SIZE 0x1 +#define XT2000_NVRAM_BINFO_ETH_ADDR_OFFSET 0x02 +#define XT2000_NVRAM_BINFO_ETH_ADDR_SIZE 0x6 +#define XT2000_NVRAM_BINFO_SN_OFFSET 0x10 +#define XT2000_NVRAM_BINFO_SN_SIZE 0xE +#define XT2000_NVRAM_BINFO_CRC_OFFSET 0x1E +#define XT2000_NVRAM_BINFO_CRC_SIZE 0x2 +#endif /*0*/ + +#if !defined(__ASSEMBLY__) && !defined(_NOCLANGUAGE) +typedef struct xt2000_nvram_binfo { + unsigned char version; + unsigned char reserved1; + unsigned char eth_addr[6]; + unsigned char reserved8[8]; + unsigned char serialno[14]; + unsigned char crc[2]; /* 16-bit CRC */ +} xt2000_nvram_binfo; +#endif /*!__ASSEMBLY__ && !_NOCLANGUAGE*/ + + +#endif /*_INC_XT2000_H_*/ + diff --git a/include/asm-xtensa/xtensa/xtboard.h b/include/asm-xtensa/xtensa/xtboard.h new file mode 100644 index 000000000000..22469c175307 --- /dev/null +++ b/include/asm-xtensa/xtensa/xtboard.h @@ -0,0 +1,120 @@ +#ifndef _xtboard_h_included_ +#define _xtboard_h_included_ + +/* + * THIS FILE IS GENERATED -- DO NOT MODIFY BY HAND + * + * xtboard.h -- Routines for getting useful information from the board. + * + * 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) 2002 Tensilica Inc. + */ + + +#include + +#define XTBOARD_RTC_ERROR -1 +#define XTBOARD_RTC_STOPPED -2 + + +/* xt2000-i2cdev.c: */ +typedef void XtboardDelayFunc( unsigned ); +extern XtboardDelayFunc* xtboard_set_nsdelay_func( XtboardDelayFunc *delay_fn ); +extern int xtboard_i2c_read (unsigned id, unsigned char *buf, unsigned addr, unsigned size); +extern int xtboard_i2c_write(unsigned id, unsigned char *buf, unsigned addr, unsigned size); +extern int xtboard_i2c_wait_nvram_ack(unsigned id, unsigned swtimer); + +/* xtboard.c: */ +extern int xtboard_nvram_read (unsigned addr, unsigned len, unsigned char *buf); +extern int xtboard_nvram_write(unsigned addr, unsigned len, unsigned char *buf); +extern int xtboard_nvram_binfo_read (xt2000_nvram_binfo *buf); +extern int xtboard_nvram_binfo_write(xt2000_nvram_binfo *buf); +extern int xtboard_nvram_binfo_valid(xt2000_nvram_binfo *buf); +extern int xtboard_ethermac_get(unsigned char *buf); +extern int xtboard_ethermac_set(unsigned char *buf); + +/*+*---------------------------------------------------------------------------- +/ Function: xtboard_get_rtc_time +/ +/ Description: Get time stored in real-time clock. +/ +/ Returns: time in seconds stored in real-time clock. +/-**----------------------------------------------------------------------------*/ + +extern unsigned xtboard_get_rtc_time(void); + +/*+*---------------------------------------------------------------------------- +/ Function: xtboard_set_rtc_time +/ +/ Description: Set time stored in real-time clock. +/ +/ Parameters: time -- time in seconds to store to real-time clock +/ +/ Returns: 0 on success, xtboard_i2c_write() error code otherwise. +/-**----------------------------------------------------------------------------*/ + +extern int xtboard_set_rtc_time(unsigned time); + + +/* xtfreq.c: */ +/*+*---------------------------------------------------------------------------- +/ Function: xtboard_measure_sys_clk +/ +/ Description: Get frequency of system clock. +/ +/ Parameters: none +/ +/ Returns: frequency of system clock. +/-**----------------------------------------------------------------------------*/ + +extern unsigned xtboard_measure_sys_clk(void); + + +#if 0 /* old stuff from xtboard.c: */ + +/*+*---------------------------------------------------------------------------- +/ Function: xtboard_nvram valid +/ +/ Description: Determines if data in NVRAM is valid. +/ +/ Parameters: delay -- 10us delay function +/ +/ Returns: 1 if NVRAM is valid, 0 otherwise +/-**----------------------------------------------------------------------------*/ + +extern unsigned xtboard_nvram_valid(void (*delay)( void )); + +/*+*---------------------------------------------------------------------------- +/ Function: xtboard_get_nvram_contents +/ +/ Description: Returns contents of NVRAM. +/ +/ Parameters: buf -- buffer to NVRAM contents. +/ delay -- 10us delay function +/ +/ Returns: 1 if NVRAM is valid, 0 otherwise +/-**----------------------------------------------------------------------------*/ + +extern unsigned xtboard_get_nvram_contents(unsigned char *buf, void (*delay)( void )); + +/*+*---------------------------------------------------------------------------- +/ Function: xtboard_get_ether_addr +/ +/ Description: Returns ethernet address of board. +/ +/ Parameters: buf -- buffer to store ethernet address +/ delay -- 10us delay function +/ +/ Returns: nothing. +/-**----------------------------------------------------------------------------*/ + +extern void xtboard_get_ether_addr(unsigned char *buf, void (*delay)( void )); + +#endif /*0*/ + + +#endif /*_xtboard_h_included_*/ + diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 7e736e201c46..c1e82c514443 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -69,6 +69,11 @@ extern void remove_arg_zero(struct linux_binprm *); extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); extern int flush_old_exec(struct linux_binprm * bprm); +extern int suid_dumpable; +#define SUID_DUMP_DISABLE 0 /* No setuid dumping */ +#define SUID_DUMP_USER 1 /* Dump as user of process */ +#define SUID_DUMP_ROOT 2 /* Dump as root */ + /* Stack area protections */ #define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */ #define EXSTACK_DISABLE_X 1 /* Disable executable stacks */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4a99b76c5a33..60272141ff19 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -285,16 +285,12 @@ enum blk_queue_state { Queue_up, }; -#define BLK_TAGS_PER_LONG (sizeof(unsigned long) * 8) -#define BLK_TAGS_MASK (BLK_TAGS_PER_LONG - 1) - struct blk_queue_tag { struct request **tag_index; /* map of busy tags */ unsigned long *tag_map; /* bit map of free/busy tags */ struct list_head busy_list; /* fifo list of busy tags */ int busy; /* current depth */ int max_depth; /* what we will send to device */ - int real_max_depth; /* what the array can hold */ atomic_t refcnt; /* map can be shared */ }; @@ -396,6 +392,7 @@ struct request_queue */ unsigned int sg_timeout; unsigned int sg_reserved_size; + int node; struct list_head drain_list; @@ -615,6 +612,8 @@ static inline void blkdev_dequeue_request(struct request *req) /* * Access functions for manipulating queue properties */ +extern request_queue_t *blk_init_queue_node(request_fn_proc *rfn, + spinlock_t *lock, int node_id); extern request_queue_t *blk_init_queue(request_fn_proc *, spinlock_t *); extern void blk_cleanup_queue(request_queue_t *); extern void blk_queue_make_request(request_queue_t *, make_request_fn *); @@ -646,7 +645,8 @@ extern void blk_wait_queue_drained(request_queue_t *, int); extern void blk_finish_queue_drain(request_queue_t *); int blk_get_queue(request_queue_t *); -request_queue_t *blk_alloc_queue(int); +request_queue_t *blk_alloc_queue(int gfp_mask); +request_queue_t *blk_alloc_queue_node(int,int); #define blk_put_queue(q) blk_cleanup_queue((q)) /* diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 0dd8ca1a3d5a..500f451ce0c0 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -67,6 +67,15 @@ extern void * __init __alloc_bootmem_node (pg_data_t *pgdat, unsigned long size, __alloc_bootmem_node((pgdat), (x), PAGE_SIZE, 0) #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ +#ifdef CONFIG_HAVE_ARCH_ALLOC_REMAP +extern void *alloc_remap(int nid, unsigned long size); +#else +static inline void *alloc_remap(int nid, unsigned long size) +{ + return NULL; +} +#endif + extern unsigned long __initdata nr_kernel_pages; extern unsigned long __initdata nr_all_pages; diff --git a/include/linux/dqblk_v1.h b/include/linux/dqblk_v1.h index 42fbf4797156..57f1250d5a52 100644 --- a/include/linux/dqblk_v1.h +++ b/include/linux/dqblk_v1.h @@ -11,6 +11,12 @@ /* Root squash turned on */ #define V1_DQF_RSQUASH 1 +/* Numbers of blocks needed for updates */ +#define V1_INIT_ALLOC 1 +#define V1_INIT_REWRITE 1 +#define V1_DEL_ALLOC 0 +#define V1_DEL_REWRITE 2 + /* Special information about quotafile */ struct v1_mem_dqinfo { }; diff --git a/include/linux/dqblk_v2.h b/include/linux/dqblk_v2.h index 4a6c5f6867bb..4f853322cb7f 100644 --- a/include/linux/dqblk_v2.h +++ b/include/linux/dqblk_v2.h @@ -10,6 +10,12 @@ /* id numbers of quota format */ #define QFMT_VFS_V0 2 +/* Numbers of blocks needed for updates */ +#define V2_INIT_ALLOC 4 +#define V2_INIT_REWRITE 2 +#define V2_DEL_ALLOC 0 +#define V2_DEL_REWRITE 6 + /* Inmemory copy of version specific information */ struct v2_mem_dqinfo { unsigned int dqi_blocks; diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index fab43527e597..a657130ba03a 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -300,18 +300,19 @@ struct ext2_inode { /* * Mount flags */ -#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ -#define EXT2_MOUNT_OLDALLOC 0x0002 /* Don't use the new Orlov allocator */ -#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ -#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ -#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ -#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ -#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ -#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ -#define EXT2_MOUNT_NOBH 0x0100 /* No buffer_heads */ -#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ -#define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ -#define EXT2_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ +#define EXT2_MOUNT_CHECK 0x000001 /* Do mount-time checks */ +#define EXT2_MOUNT_OLDALLOC 0x000002 /* Don't use the new Orlov allocator */ +#define EXT2_MOUNT_GRPID 0x000004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x000008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x000010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x000020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x000040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x000080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NOBH 0x000100 /* No buffer_heads */ +#define EXT2_MOUNT_NO_UID32 0x000200 /* Disable 32-bit UIDs */ +#define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */ +#define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */ +#define EXT2_MOUNT_XIP 0x010000 /* Execute in place */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 74ad31781e3e..4b6e1ab216a5 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -358,6 +358,7 @@ struct ext3_inode { #define EXT3_MOUNT_RESERVATION 0x10000 /* Preallocation */ #define EXT3_MOUNT_BARRIER 0x20000 /* Use block barriers */ #define EXT3_MOUNT_NOBH 0x40000 /* No bufferheads */ +#define EXT3_MOUNT_QUOTA 0x80000 /* Some quota option set */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H diff --git a/include/linux/ext3_jbd.h b/include/linux/ext3_jbd.h index e8292af9033b..c8307c02dd07 100644 --- a/include/linux/ext3_jbd.h +++ b/include/linux/ext3_jbd.h @@ -42,15 +42,15 @@ * superblock only gets updated once, of course, so don't bother * counting that again for the quota updates. */ -#define EXT3_DATA_TRANS_BLOCKS (EXT3_SINGLEDATA_TRANS_BLOCKS + \ +#define EXT3_DATA_TRANS_BLOCKS(sb) (EXT3_SINGLEDATA_TRANS_BLOCKS + \ EXT3_XATTR_TRANS_BLOCKS - 2 + \ - 2*EXT3_QUOTA_TRANS_BLOCKS) + 2*EXT3_QUOTA_TRANS_BLOCKS(sb)) /* Delete operations potentially hit one directory's namespace plus an * entire inode, plus arbitrary amounts of bitmap/indirection data. Be * generous. We can grow the delete transaction later if necessary. */ -#define EXT3_DELETE_TRANS_BLOCKS (2 * EXT3_DATA_TRANS_BLOCKS + 64) +#define EXT3_DELETE_TRANS_BLOCKS(sb) (2 * EXT3_DATA_TRANS_BLOCKS(sb) + 64) /* Define an arbitrary limit for the amount of data we will anticipate * writing to any given transaction. For unbounded transactions such as @@ -74,14 +74,17 @@ #ifdef CONFIG_QUOTA /* Amount of blocks needed for quota update - we know that the structure was * allocated so we need to update only inode+data */ -#define EXT3_QUOTA_TRANS_BLOCKS 2 +#define EXT3_QUOTA_TRANS_BLOCKS(sb) (test_opt(sb, QUOTA) ? 2 : 0) /* Amount of blocks needed for quota insert/delete - we do some block writes * but inode, sb and group updates are done only once */ -#define EXT3_QUOTA_INIT_BLOCKS (DQUOT_MAX_WRITES*\ - (EXT3_SINGLEDATA_TRANS_BLOCKS-3)+3) +#define EXT3_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\ + (EXT3_SINGLEDATA_TRANS_BLOCKS-3)+3+DQUOT_INIT_REWRITE) : 0) +#define EXT3_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\ + (EXT3_SINGLEDATA_TRANS_BLOCKS-3)+3+DQUOT_DEL_REWRITE) : 0) #else -#define EXT3_QUOTA_TRANS_BLOCKS 0 -#define EXT3_QUOTA_INIT_BLOCKS 0 +#define EXT3_QUOTA_TRANS_BLOCKS(sb) 0 +#define EXT3_QUOTA_INIT_BLOCKS(sb) 0 +#define EXT3_QUOTA_DEL_BLOCKS(sb) 0 #endif int diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index 704fb76b6334..8a7c82151de9 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -25,6 +25,10 @@ #ifdef __KERNEL__ +#ifndef force_o_largefile +#define force_o_largefile() (BITS_PER_LONG != 32) +#endif + #if BITS_PER_LONG == 32 #define IS_GETLK32(cmd) ((cmd) == F_GETLK) #define IS_SETLK32(cmd) ((cmd) == F_SETLK) diff --git a/include/linux/fs.h b/include/linux/fs.h index e5a8db00df29..3ae8e37bdfc8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -220,6 +220,7 @@ extern int dir_notify_enable; struct iovec; struct nameidata; +struct kiocb; struct pipe_inode_info; struct poll_table_struct; struct kstatfs; @@ -240,7 +241,7 @@ typedef int (get_block_t)(struct inode *inode, sector_t iblock, typedef int (get_blocks_t)(struct inode *inode, sector_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, int create); -typedef void (dio_iodone_t)(struct inode *inode, loff_t offset, +typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, ssize_t bytes, void *private); /* @@ -302,7 +303,6 @@ struct iattr { struct page; struct address_space; struct writeback_control; -struct kiocb; struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); @@ -330,6 +330,8 @@ struct address_space_operations { int (*releasepage) (struct page *, int); ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, loff_t offset, unsigned long nr_segs); + struct page* (*get_xip_page)(struct address_space *, sector_t, + int); }; struct backing_dev_info; @@ -581,7 +583,6 @@ struct file { atomic_t f_count; unsigned int f_flags; mode_t f_mode; - int f_error; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; @@ -884,7 +885,9 @@ struct block_device_operations { int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); + long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); long (*compat_ioctl) (struct file *, unsigned, unsigned long); + int (*direct_access) (struct block_device *, sector_t, unsigned long *); int (*media_changed) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); struct module *owner; @@ -1025,6 +1028,7 @@ struct super_operations { #define I_FREEING 16 #define I_CLEAR 32 #define I_NEW 64 +#define I_WILL_FREE 128 #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) @@ -1495,6 +1499,23 @@ extern loff_t remote_llseek(struct file *file, loff_t offset, int origin); extern int generic_file_open(struct inode * inode, struct file * filp); extern int nonseekable_open(struct inode * inode, struct file * filp); +#ifdef CONFIG_FS_XIP +extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len, + loff_t *ppos); +extern ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos, + size_t count, read_actor_t actor, + void *target); +extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma); +extern ssize_t xip_file_write(struct file *filp, const char __user *buf, + size_t len, loff_t *ppos); +extern int xip_truncate_page(struct address_space *mapping, loff_t from); +#else +static inline int xip_truncate_page(struct address_space *mapping, loff_t from) +{ + return 0; +} +#endif + static inline void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor) diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 47dedaf971d6..01796c41c951 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -224,7 +224,7 @@ static inline void free_disk_stats(struct gendisk *disk) extern void disk_round_stats(struct gendisk *disk); /* drivers/block/genhd.c */ -extern int get_blkdev_list(char *); +extern int get_blkdev_list(char *, int); extern void add_disk(struct gendisk *disk); extern void del_gendisk(struct gendisk *gp); extern void unlink_gendisk(struct gendisk *gp); @@ -403,6 +403,7 @@ extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); extern void add_partition(struct gendisk *, int, sector_t, sector_t); extern void delete_partition(struct gendisk *, int); +extern struct gendisk *alloc_disk_node(int minors, int node_id); extern struct gendisk *alloc_disk(int minors); extern struct kobject *get_disk(struct gendisk *disk); extern void put_disk(struct gendisk *disk); diff --git a/include/linux/i2o-dev.h b/include/linux/i2o-dev.h index ef7f644dd873..36fd18cdad28 100644 --- a/include/linux/i2o-dev.h +++ b/include/linux/i2o-dev.h @@ -24,6 +24,13 @@ #define MAX_I2O_CONTROLLERS 32 //#include +#ifndef __KERNEL__ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +#endif /* __KERNEL__ */ /* * I2O Control IOCTLs and structures @@ -113,6 +120,10 @@ struct i2o_evt_get { int lost; }; +typedef struct i2o_sg_io_hdr { + unsigned int flags; /* see I2O_DPT_SG_IO_FLAGS */ +} i2o_sg_io_hdr_t; + /************************************************************************** * HRT related constants and structures **************************************************************************/ @@ -126,14 +137,6 @@ struct i2o_evt_get { #define I2O_BUS_CARDBUS 7 #define I2O_BUS_UNKNOWN 0x80 -#ifndef __KERNEL__ - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#endif /* __KERNEL__ */ - typedef struct _i2o_pci_bus { u8 PciFunctionNumber; u8 PciDeviceNumber; @@ -333,7 +336,7 @@ typedef struct _i2o_status_block { #define I2O_CLASS_ATE_PERIPHERAL 0x061 #define I2O_CLASS_FLOPPY_CONTROLLER 0x070 #define I2O_CLASS_FLOPPY_DEVICE 0x071 -#define I2O_CLASS_BUS_ADAPTER_PORT 0x080 +#define I2O_CLASS_BUS_ADAPTER 0x080 #define I2O_CLASS_PEER_TRANSPORT_AGENT 0x090 #define I2O_CLASS_PEER_TRANSPORT 0x091 #define I2O_CLASS_END 0xfff @@ -399,4 +402,26 @@ typedef struct _i2o_status_block { #define ADAPTER_STATE_FAILED 0x10 #define ADAPTER_STATE_FAULTED 0x11 +/* + * Software module types + */ +#define I2O_SOFTWARE_MODULE_IRTOS 0x11 +#define I2O_SOFTWARE_MODULE_IOP_PRIVATE 0x22 +#define I2O_SOFTWARE_MODULE_IOP_CONFIG 0x23 + +/* + * Vendors + */ +#define I2O_VENDOR_DPT 0x001b + +/* + * DPT / Adaptec specific values for i2o_sg_io_hdr flags. + */ +#define I2O_DPT_SG_FLAG_INTERPRET 0x00010000 +#define I2O_DPT_SG_FLAG_PHYSICAL 0x00020000 + +#define I2O_DPT_FLASH_FRAG_SIZE 0x10000 +#define I2O_DPT_FLASH_READ 0x0101 +#define I2O_DPT_FLASH_WRITE 0x0102 + #endif /* _I2O_DEV_H */ diff --git a/include/linux/i2o.h b/include/linux/i2o.h index ea9a3ad4b67f..bdc286ec947c 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -119,12 +119,21 @@ struct i2o_driver { }; /* - * Contains all information which are necessary for DMA operations + * Contains DMA mapped address information */ struct i2o_dma { void *virt; dma_addr_t phys; - u32 len; + size_t len; +}; + +/* + * Contains IO mapped address information + */ +struct i2o_io { + void __iomem *virt; + unsigned long phys; + unsigned long len; }; /* @@ -147,28 +156,25 @@ struct i2o_controller { struct pci_dev *pdev; /* PCI device */ - unsigned int short_req:1; /* use small block sizes */ + unsigned int promise:1; /* Promise controller */ + unsigned int adaptec:1; /* DPT / Adaptec controller */ + unsigned int raptor:1; /* split bar */ unsigned int no_quiesce:1; /* dont quiesce before reset */ - unsigned int raptor:1; /* split bar */ - unsigned int promise:1; /* Promise controller */ - -#ifdef CONFIG_MTRR - int mtrr_reg0; - int mtrr_reg1; -#endif + unsigned int short_req:1; /* use small block sizes */ + unsigned int limit_sectors:1; /* limit number of sectors / request */ + unsigned int pae_support:1; /* controller has 64-bit SGL support */ struct list_head devices; /* list of I2O devices */ - - struct notifier_block *event_notifer; /* Events */ - atomic_t users; struct list_head list; /* Controller list */ - void __iomem *post_port; /* Inbout port address */ - void __iomem *reply_port; /* Outbound port address */ - void __iomem *irq_mask; /* Interrupt register address */ + + void __iomem *in_port; /* Inbout port address */ + void __iomem *out_port; /* Outbound port address */ + void __iomem *irq_status; /* Interrupt status register address */ + void __iomem *irq_mask; /* Interrupt mask register address */ /* Dynamic LCT related data */ - struct i2o_dma status; /* status of IOP */ + struct i2o_dma status; /* IOP status block */ struct i2o_dma hrt; /* HW Resource Table */ i2o_lct *lct; /* Logical Config Table */ @@ -176,21 +182,19 @@ struct i2o_controller { struct semaphore lct_lock; /* Lock for LCT updates */ struct i2o_dma status_block; /* IOP status block */ - struct i2o_dma base; /* controller messaging unit */ - struct i2o_dma in_queue; /* inbound message queue Host->IOP */ + struct i2o_io base; /* controller messaging unit */ + struct i2o_io in_queue; /* inbound message queue Host->IOP */ struct i2o_dma out_queue; /* outbound message queue IOP->Host */ - unsigned int battery:1; /* Has a battery backup */ + unsigned int battery:1; /* Has a battery backup */ unsigned int io_alloc:1; /* An I/O resource was allocated */ unsigned int mem_alloc:1; /* A memory resource was allocated */ struct resource io_resource; /* I/O resource allocated to the IOP */ struct resource mem_resource; /* Mem resource allocated to the IOP */ - struct proc_dir_entry *proc_entry; /* /proc dir */ - - struct list_head bus_list; /* list of busses on IOP */ struct device device; + struct class_device classdev; /* I2O controller class */ struct i2o_device *exec; /* Executive */ #if BITS_PER_LONG == 64 spinlock_t context_list_lock; /* lock for context_list */ @@ -241,9 +245,10 @@ struct i2o_sys_tbl { extern struct list_head i2o_controllers; /* Message functions */ -static inline u32 i2o_msg_get(struct i2o_controller *, struct i2o_message __iomem **); -extern u32 i2o_msg_get_wait(struct i2o_controller *, struct i2o_message __iomem **, - int); +static inline u32 i2o_msg_get(struct i2o_controller *, + struct i2o_message __iomem **); +extern u32 i2o_msg_get_wait(struct i2o_controller *, + struct i2o_message __iomem **, int); static inline void i2o_msg_post(struct i2o_controller *, u32); static inline int i2o_msg_post_wait(struct i2o_controller *, u32, unsigned long); @@ -252,15 +257,6 @@ extern int i2o_msg_post_wait_mem(struct i2o_controller *, u32, unsigned long, extern void i2o_msg_nop(struct i2o_controller *, u32); static inline void i2o_flush_reply(struct i2o_controller *, u32); -/* DMA handling functions */ -static inline int i2o_dma_alloc(struct device *, struct i2o_dma *, size_t, - unsigned int); -static inline void i2o_dma_free(struct device *, struct i2o_dma *); -int i2o_dma_realloc(struct device *, struct i2o_dma *, size_t, unsigned int); - -static inline int i2o_dma_map(struct device *, struct i2o_dma *); -static inline void i2o_dma_unmap(struct device *, struct i2o_dma *); - /* IOP functions */ extern int i2o_status_get(struct i2o_controller *); @@ -285,6 +281,16 @@ static inline u32 i2o_ptr_high(void *ptr) { return (u32) ((u64) ptr >> 32); }; + +static inline u32 i2o_dma_low(dma_addr_t dma_addr) +{ + return (u32) (u64) dma_addr; +}; + +static inline u32 i2o_dma_high(dma_addr_t dma_addr) +{ + return (u32) ((u64) dma_addr >> 32); +}; #else static inline u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr) { @@ -315,8 +321,246 @@ static inline u32 i2o_ptr_high(void *ptr) { return 0; }; + +static inline u32 i2o_dma_low(dma_addr_t dma_addr) +{ + return (u32) dma_addr; +}; + +static inline u32 i2o_dma_high(dma_addr_t dma_addr) +{ + return 0; +}; #endif +/** + * i2o_sg_tablesize - Calculate the maximum number of elements in a SGL + * @c: I2O controller for which the calculation should be done + * @body_size: maximum body size used for message in 32-bit words. + * + * Return the maximum number of SG elements in a SG list. + */ +static inline u16 i2o_sg_tablesize(struct i2o_controller *c, u16 body_size) +{ + i2o_status_block *sb = c->status_block.virt; + u16 sg_count = + (sb->inbound_frame_size - sizeof(struct i2o_message) / 4) - + body_size; + + if (c->pae_support) { + /* + * for 64-bit a SG attribute element must be added and each + * SG element needs 12 bytes instead of 8. + */ + sg_count -= 2; + sg_count /= 3; + } else + sg_count /= 2; + + if (c->short_req && (sg_count > 8)) + sg_count = 8; + + return sg_count; +}; + +/** + * i2o_dma_map_single - Map pointer to controller and fill in I2O message. + * @c: I2O controller + * @ptr: pointer to the data which should be mapped + * @size: size of data in bytes + * @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE + * @sg_ptr: pointer to the SG list inside the I2O message + * + * This function does all necessary DMA handling and also writes the I2O + * SGL elements into the I2O message. For details on DMA handling see also + * dma_map_single(). The pointer sg_ptr will only be set to the end of the + * SG list if the allocation was successful. + * + * Returns DMA address which must be checked for failures using + * dma_mapping_error(). + */ +static inline dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr, + size_t size, + enum dma_data_direction direction, + u32 __iomem ** sg_ptr) +{ + u32 sg_flags; + u32 __iomem *mptr = *sg_ptr; + dma_addr_t dma_addr; + + switch (direction) { + case DMA_TO_DEVICE: + sg_flags = 0xd4000000; + break; + case DMA_FROM_DEVICE: + sg_flags = 0xd0000000; + break; + default: + return 0; + } + + dma_addr = dma_map_single(&c->pdev->dev, ptr, size, direction); + if (!dma_mapping_error(dma_addr)) { +#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 + if ((sizeof(dma_addr_t) > 4) && c->pae_support) { + writel(0x7C020002, mptr++); + writel(PAGE_SIZE, mptr++); + } +#endif + + writel(sg_flags | size, mptr++); + writel(i2o_dma_low(dma_addr), mptr++); +#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 + if ((sizeof(dma_addr_t) > 4) && c->pae_support) + writel(i2o_dma_high(dma_addr), mptr++); +#endif + *sg_ptr = mptr; + } + return dma_addr; +}; + +/** + * i2o_dma_map_sg - Map a SG List to controller and fill in I2O message. + * @c: I2O controller + * @sg: SG list to be mapped + * @sg_count: number of elements in the SG list + * @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE + * @sg_ptr: pointer to the SG list inside the I2O message + * + * This function does all necessary DMA handling and also writes the I2O + * SGL elements into the I2O message. For details on DMA handling see also + * dma_map_sg(). The pointer sg_ptr will only be set to the end of the SG + * list if the allocation was successful. + * + * Returns 0 on failure or 1 on success. + */ +static inline int i2o_dma_map_sg(struct i2o_controller *c, + struct scatterlist *sg, int sg_count, + enum dma_data_direction direction, + u32 __iomem ** sg_ptr) +{ + u32 sg_flags; + u32 __iomem *mptr = *sg_ptr; + + switch (direction) { + case DMA_TO_DEVICE: + sg_flags = 0x14000000; + break; + case DMA_FROM_DEVICE: + sg_flags = 0x10000000; + break; + default: + return 0; + } + + sg_count = dma_map_sg(&c->pdev->dev, sg, sg_count, direction); + if (!sg_count) + return 0; + +#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 + if ((sizeof(dma_addr_t) > 4) && c->pae_support) { + writel(0x7C020002, mptr++); + writel(PAGE_SIZE, mptr++); + } +#endif + + while (sg_count-- > 0) { + if (!sg_count) + sg_flags |= 0xC0000000; + writel(sg_flags | sg_dma_len(sg), mptr++); + writel(i2o_dma_low(sg_dma_address(sg)), mptr++); +#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 + if ((sizeof(dma_addr_t) > 4) && c->pae_support) + writel(i2o_dma_high(sg_dma_address(sg)), mptr++); +#endif + sg++; + } + *sg_ptr = mptr; + + return 1; +}; + +/** + * i2o_dma_alloc - Allocate DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: i2o_dma struct which should get the DMA buffer + * @len: length of the new DMA memory + * @gfp_mask: GFP mask + * + * Allocate a coherent DMA memory and write the pointers into addr. + * + * Returns 0 on success or -ENOMEM on failure. + */ +static inline int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, + size_t len, unsigned int gfp_mask) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int dma_64 = 0; + + if ((sizeof(dma_addr_t) > 4) && (pdev->dma_mask == DMA_64BIT_MASK)) { + dma_64 = 1; + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) + return -ENOMEM; + } + + addr->virt = dma_alloc_coherent(dev, len, &addr->phys, gfp_mask); + + if ((sizeof(dma_addr_t) > 4) && dma_64) + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK)) + printk(KERN_WARNING "i2o: unable to set 64-bit DMA"); + + if (!addr->virt) + return -ENOMEM; + + memset(addr->virt, 0, len); + addr->len = len; + + return 0; +}; + +/** + * i2o_dma_free - Free DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: i2o_dma struct which contains the DMA buffer + * + * Free a coherent DMA memory and set virtual address of addr to NULL. + */ +static inline void i2o_dma_free(struct device *dev, struct i2o_dma *addr) +{ + if (addr->virt) { + if (addr->phys) + dma_free_coherent(dev, addr->len, addr->virt, + addr->phys); + else + kfree(addr->virt); + addr->virt = NULL; + } +}; + +/** + * i2o_dma_realloc - Realloc DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: pointer to a i2o_dma struct DMA buffer + * @len: new length of memory + * @gfp_mask: GFP mask + * + * If there was something allocated in the addr, free it first. If len > 0 + * than try to allocate it and write the addresses back to the addr + * structure. If len == 0 set the virtual address to NULL. + * + * Returns the 0 on success or negative error code on failure. + */ +static inline int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, + size_t len, unsigned int gfp_mask) +{ + i2o_dma_free(dev, addr); + + if (len) + return i2o_dma_alloc(dev, addr, len, gfp_mask); + + return 0; +}; + /* I2O driver (OSM) functions */ extern int i2o_driver_register(struct i2o_driver *); extern void i2o_driver_unregister(struct i2o_driver *); @@ -385,49 +629,11 @@ extern int i2o_device_claim_release(struct i2o_device *); /* Exec OSM functions */ extern int i2o_exec_lct_get(struct i2o_controller *); -/* device to i2o_device and driver to i2o_driver convertion functions */ +/* device / driver / kobject conversion functions */ #define to_i2o_driver(drv) container_of(drv,struct i2o_driver, driver) #define to_i2o_device(dev) container_of(dev, struct i2o_device, device) - -/* - * Messenger inlines - */ -static inline u32 I2O_POST_READ32(struct i2o_controller *c) -{ - rmb(); - return readl(c->post_port); -}; - -static inline void I2O_POST_WRITE32(struct i2o_controller *c, u32 val) -{ - wmb(); - writel(val, c->post_port); -}; - -static inline u32 I2O_REPLY_READ32(struct i2o_controller *c) -{ - rmb(); - return readl(c->reply_port); -}; - -static inline void I2O_REPLY_WRITE32(struct i2o_controller *c, u32 val) -{ - wmb(); - writel(val, c->reply_port); -}; - -static inline u32 I2O_IRQ_READ32(struct i2o_controller *c) -{ - rmb(); - return readl(c->irq_mask); -}; - -static inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 val) -{ - wmb(); - writel(val, c->irq_mask); - wmb(); -}; +#define to_i2o_controller(dev) container_of(dev, struct i2o_controller, device) +#define kobj_to_i2o_device(kobj) to_i2o_device(container_of(kobj, struct device, kobj)) /** * i2o_msg_get - obtain an I2O message from the IOP @@ -443,11 +649,11 @@ static inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 val) * available returns I2O_QUEUE_EMPTY and msg is leaved untouched. */ static inline u32 i2o_msg_get(struct i2o_controller *c, - struct i2o_message __iomem **msg) + struct i2o_message __iomem ** msg) { - u32 m; + u32 m = readl(c->in_port); - if ((m = I2O_POST_READ32(c)) != I2O_QUEUE_EMPTY) + if (m != I2O_QUEUE_EMPTY) *msg = c->in_queue.virt + m; return m; @@ -462,7 +668,7 @@ static inline u32 i2o_msg_get(struct i2o_controller *c, */ static inline void i2o_msg_post(struct i2o_controller *c, u32 m) { - I2O_POST_WRITE32(c, m); + writel(m, c->in_port); }; /** @@ -491,12 +697,10 @@ static inline int i2o_msg_post_wait(struct i2o_controller *c, u32 m, * The I2O controller must be informed that the reply message is not needed * anymore. If you forget to flush the reply, the message frame can't be * used by the controller anymore and is therefore lost. - * - * FIXME: is there a timeout after which the controller reuse the message? */ static inline void i2o_flush_reply(struct i2o_controller *c, u32 m) { - I2O_REPLY_WRITE32(c, m); + writel(m, c->out_port); }; /** @@ -530,97 +734,13 @@ static inline struct i2o_message *i2o_msg_out_to_virt(struct i2o_controller *c, * work for receive side messages as they are kmalloc objects * in a different pool. */ -static inline struct i2o_message __iomem *i2o_msg_in_to_virt(struct i2o_controller *c, - u32 m) +static inline struct i2o_message __iomem *i2o_msg_in_to_virt(struct + i2o_controller *c, + u32 m) { return c->in_queue.virt + m; }; -/** - * i2o_dma_alloc - Allocate DMA memory - * @dev: struct device pointer to the PCI device of the I2O controller - * @addr: i2o_dma struct which should get the DMA buffer - * @len: length of the new DMA memory - * @gfp_mask: GFP mask - * - * Allocate a coherent DMA memory and write the pointers into addr. - * - * Returns 0 on success or -ENOMEM on failure. - */ -static inline int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, - size_t len, unsigned int gfp_mask) -{ - addr->virt = dma_alloc_coherent(dev, len, &addr->phys, gfp_mask); - if (!addr->virt) - return -ENOMEM; - - memset(addr->virt, 0, len); - addr->len = len; - - return 0; -}; - -/** - * i2o_dma_free - Free DMA memory - * @dev: struct device pointer to the PCI device of the I2O controller - * @addr: i2o_dma struct which contains the DMA buffer - * - * Free a coherent DMA memory and set virtual address of addr to NULL. - */ -static inline void i2o_dma_free(struct device *dev, struct i2o_dma *addr) -{ - if (addr->virt) { - if (addr->phys) - dma_free_coherent(dev, addr->len, addr->virt, - addr->phys); - else - kfree(addr->virt); - addr->virt = NULL; - } -}; - -/** - * i2o_dma_map - Map the memory to DMA - * @dev: struct device pointer to the PCI device of the I2O controller - * @addr: i2o_dma struct which should be mapped - * - * Map the memory in addr->virt to coherent DMA memory and write the - * physical address into addr->phys. - * - * Returns 0 on success or -ENOMEM on failure. - */ -static inline int i2o_dma_map(struct device *dev, struct i2o_dma *addr) -{ - if (!addr->virt) - return -EFAULT; - - if (!addr->phys) - addr->phys = dma_map_single(dev, addr->virt, addr->len, - DMA_BIDIRECTIONAL); - if (!addr->phys) - return -ENOMEM; - - return 0; -}; - -/** - * i2o_dma_unmap - Unmap the DMA memory - * @dev: struct device pointer to the PCI device of the I2O controller - * @addr: i2o_dma struct which should be unmapped - * - * Unmap the memory in addr->virt from DMA memory. - */ -static inline void i2o_dma_unmap(struct device *dev, struct i2o_dma *addr) -{ - if (!addr->virt) - return; - - if (addr->phys) { - dma_unmap_single(dev, addr->phys, addr->len, DMA_BIDIRECTIONAL); - addr->phys = 0; - } -}; - /* * Endian handling wrapped into the macro - keeps the core code * cleaner. @@ -772,6 +892,14 @@ extern void i2o_debug_state(struct i2o_controller *c); #define I2O_CMD_SCSI_ABORT 0x83 #define I2O_CMD_SCSI_BUSRESET 0x27 +/* + * Bus Adapter Class + */ +#define I2O_CMD_BUS_ADAPTER_RESET 0x85 +#define I2O_CMD_BUS_RESET 0x87 +#define I2O_CMD_BUS_SCAN 0x89 +#define I2O_CMD_BUS_QUIESCE 0x8b + /* * Random Block Storage Class */ @@ -784,7 +912,7 @@ extern void i2o_debug_state(struct i2o_controller *c); #define I2O_CMD_BLOCK_MEJECT 0x43 #define I2O_CMD_BLOCK_POWER 0x70 -#define I2O_PRIVATE_MSG 0xFF +#define I2O_CMD_PRIVATE 0xFF /* Command status values */ @@ -922,7 +1050,7 @@ extern void i2o_debug_state(struct i2o_controller *c); #define I2OVER15 0x0001 #define I2OVER20 0x0002 -/* Default is 1.5, FIXME: Need support for both 1.5 and 2.0 */ +/* Default is 1.5 */ #define I2OVERSION I2OVER15 #define SGL_OFFSET_0 I2OVERSION @@ -933,9 +1061,9 @@ extern void i2o_debug_state(struct i2o_controller *c); #define SGL_OFFSET_8 (0x0080 | I2OVERSION) #define SGL_OFFSET_9 (0x0090 | I2OVERSION) #define SGL_OFFSET_10 (0x00A0 | I2OVERSION) - -#define TRL_OFFSET_5 (0x0050 | I2OVERSION) -#define TRL_OFFSET_6 (0x0060 | I2OVERSION) +#define SGL_OFFSET_11 (0x00B0 | I2OVERSION) +#define SGL_OFFSET_12 (0x00C0 | I2OVERSION) +#define SGL_OFFSET(x) (((x)<<4) | I2OVERSION) /* Transaction Reply Lists (TRL) Control Word structure */ #define TRL_SINGLE_FIXED_LENGTH 0x00 @@ -962,17 +1090,13 @@ extern void i2o_debug_state(struct i2o_controller *c); #define ELEVEN_WORD_MSG_SIZE 0x000B0000 #define I2O_MESSAGE_SIZE(x) ((x)<<16) -/* Special TID Assignments */ - +/* special TID assignments */ #define ADAPTER_TID 0 #define HOST_TID 1 -#define MSG_FRAME_SIZE 128 /* i2o_scsi assumes >= 32 */ -#define REPLY_FRAME_SIZE 17 -#define SG_TABLESIZE 30 -#define NMBR_MSG_FRAMES 128 - -#define MSG_POOL_SIZE (MSG_FRAME_SIZE*NMBR_MSG_FRAMES*sizeof(u32)) +/* outbound queue defines */ +#define I2O_MAX_OUTBOUND_MSG_FRAMES 128 +#define I2O_OUTBOUND_MSG_FRAME_SIZE 128 /* in 32-bit words */ #define I2O_POST_WAIT_OK 0 #define I2O_POST_WAIT_TIMEOUT -ETIMEDOUT @@ -993,11 +1117,10 @@ extern void i2o_debug_state(struct i2o_controller *c); #define I2O_HRT_GET_TRIES 3 #define I2O_LCT_GET_TRIES 3 -/* request queue sizes */ +/* defines for max_sectors and max_phys_segments */ #define I2O_MAX_SECTORS 1024 -#define I2O_MAX_SEGMENTS 128 - -#define I2O_REQ_MEMPOOL_SIZE 32 +#define I2O_MAX_SECTORS_LIMITED 256 +#define I2O_MAX_PHYS_SEGMENTS MAX_PHYS_SEGMENTS #endif /* __KERNEL__ */ #endif /* _I2O_H */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 336d6e509f59..92129078d4f3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -917,7 +917,7 @@ typedef struct hwif_s { unsigned dma; void (*led_act)(void *data, int rw); -} ide_hwif_t; +} ____cacheline_maxaligned_in_smp ide_hwif_t; /* * internal ide interrupt handler type diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index 2ec265e1045f..596ca6130159 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -209,6 +209,11 @@ struct kernel_ipmi_msg #include #include +#ifdef CONFIG_PROC_FS +#include +extern struct proc_dir_entry *proc_ipmi_root; +#endif /* CONFIG_PROC_FS */ + /* Opaque type for a IPMI message user. One of these is needed to send and receive messages. */ typedef struct ipmi_user *ipmi_user_t; diff --git a/include/linux/irq.h b/include/linux/irq.h index 7fc1022be9ee..12277799c007 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -88,7 +88,6 @@ extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action); extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); extern void note_interrupt(unsigned int irq, irq_desc_t *desc, int action_ret); -extern void report_bad_irq(unsigned int irq, irq_desc_t *desc, int action_ret); extern int can_request_irq(unsigned int irq, unsigned long irqflags); extern void init_irq_proc(void); diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h index 60cc7b762e78..cc326174a808 100644 --- a/include/linux/key-ui.h +++ b/include/linux/key-ui.h @@ -1,4 +1,4 @@ -/* key-ui.h: key userspace interface stuff for use by keyfs +/* key-ui.h: key userspace interface stuff * * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -31,8 +31,10 @@ extern spinlock_t key_serial_lock; * subscribed */ struct keyring_list { - unsigned maxkeys; /* max keys this list can hold */ - unsigned nkeys; /* number of keys currently held */ + struct rcu_head rcu; /* RCU deletion hook */ + unsigned short maxkeys; /* max keys this list can hold */ + unsigned short nkeys; /* number of keys currently held */ + unsigned short delkey; /* key to be unlinked by RCU */ struct key *keys[0]; }; @@ -82,8 +84,45 @@ static inline int key_any_permission(const struct key *key, key_perm_t perm) return kperm != 0; } +static inline int key_task_groups_search(struct task_struct *tsk, gid_t gid) +{ + int ret; -extern struct key *lookup_user_key(key_serial_t id, int create, int part, + task_lock(tsk); + ret = groups_search(tsk->group_info, gid); + task_unlock(tsk); + return ret; +} + +static inline int key_task_permission(const struct key *key, + struct task_struct *context, + key_perm_t perm) +{ + key_perm_t kperm; + + if (key->uid == context->fsuid) { + kperm = key->perm >> 16; + } + else if (key->gid != -1 && + key->perm & KEY_GRP_ALL && ( + key->gid == context->fsgid || + key_task_groups_search(context, key->gid) + ) + ) { + kperm = key->perm >> 8; + } + else { + kperm = key->perm; + } + + kperm = kperm & perm & KEY_ALL; + + return kperm == perm; + +} + +extern struct key *lookup_user_key(struct task_struct *context, + key_serial_t id, int create, int partial, key_perm_t perm); extern long join_session_keyring(const char *name); diff --git a/include/linux/key.h b/include/linux/key.h index 6aa46d0e812f..970bbd916cf4 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #ifdef __KERNEL__ @@ -78,7 +78,6 @@ struct key { key_serial_t serial; /* key serial number */ struct rb_node serial_node; struct key_type *type; /* type of key */ - rwlock_t lock; /* examination vs change lock */ struct rw_semaphore sem; /* change vs change sem */ struct key_user *user; /* owner of this key */ time_t expiry; /* time at which key expires (or 0) */ @@ -86,14 +85,10 @@ struct key { gid_t gid; key_perm_t perm; /* access permissions */ unsigned short quotalen; /* length added to quota */ - unsigned short datalen; /* payload data length */ - unsigned short flags; /* status flags (change with lock writelocked) */ -#define KEY_FLAG_INSTANTIATED 0x00000001 /* set if key has been instantiated */ -#define KEY_FLAG_DEAD 0x00000002 /* set if key type has been deleted */ -#define KEY_FLAG_REVOKED 0x00000004 /* set if key had been revoked */ -#define KEY_FLAG_IN_QUOTA 0x00000008 /* set if key consumes quota */ -#define KEY_FLAG_USER_CONSTRUCT 0x00000010 /* set if key is being constructed in userspace */ -#define KEY_FLAG_NEGATIVE 0x00000020 /* set if key is negative */ + unsigned short datalen; /* payload data length + * - may not match RCU dereferenced payload + * - payload should contain own length + */ #ifdef KEY_DEBUGGING unsigned magic; @@ -101,6 +96,14 @@ struct key { #define KEY_DEBUG_MAGIC_X 0xf8e9dacbu #endif + unsigned long flags; /* status flags (change with bitops) */ +#define KEY_FLAG_INSTANTIATED 0 /* set if key has been instantiated */ +#define KEY_FLAG_DEAD 1 /* set if key type has been deleted */ +#define KEY_FLAG_REVOKED 2 /* set if key had been revoked */ +#define KEY_FLAG_IN_QUOTA 3 /* set if key consumes quota */ +#define KEY_FLAG_USER_CONSTRUCT 4 /* set if key is being constructed in userspace */ +#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ + /* the description string * - this is used to match a key against search criteria * - this should be a printable string @@ -196,10 +199,12 @@ extern int key_payload_reserve(struct key *key, size_t datalen); extern int key_instantiate_and_link(struct key *key, const void *data, size_t datalen, - struct key *keyring); + struct key *keyring, + struct key *instkey); extern int key_negate_and_link(struct key *key, unsigned timeout, - struct key *keyring); + struct key *keyring, + struct key *instkey); extern void key_revoke(struct key *key); extern void key_put(struct key *key); @@ -242,14 +247,13 @@ extern struct key *keyring_search(struct key *keyring, struct key_type *type, const char *description); -extern struct key *search_process_keyrings(struct key_type *type, - const char *description); - extern int keyring_add_key(struct key *keyring, struct key *key); extern struct key *key_lookup(key_serial_t id); +extern void keyring_replace_payload(struct key *key, void *replacement); + #define key_serial(key) ((key) ? (key)->serial : 0) /* @@ -268,14 +272,22 @@ extern void key_fsuid_changed(struct task_struct *tsk); extern void key_fsgid_changed(struct task_struct *tsk); extern void key_init(void); +#define __install_session_keyring(tsk, keyring) \ +({ \ + struct key *old_session = tsk->signal->session_keyring; \ + tsk->signal->session_keyring = keyring; \ + old_session; \ +}) + #else /* CONFIG_KEYS */ #define key_validate(k) 0 #define key_serial(k) 0 -#define key_get(k) NULL +#define key_get(k) ({ NULL; }) #define key_put(k) do { } while(0) #define alloc_uid_keyring(u) 0 #define switch_uid_keyring(u) do { } while(0) +#define __install_session_keyring(t, k) ({ NULL; }) #define copy_keys(f,t) 0 #define copy_thread_group_keys(t) 0 #define exit_keys(t) do { } while(0) diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h index 381dedc370a3..8d7c59a29e09 100644 --- a/include/linux/keyctl.h +++ b/include/linux/keyctl.h @@ -20,6 +20,16 @@ #define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */ #define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */ +/* request-key default keyrings */ +#define KEY_REQKEY_DEFL_NO_CHANGE -1 +#define KEY_REQKEY_DEFL_DEFAULT 0 +#define KEY_REQKEY_DEFL_THREAD_KEYRING 1 +#define KEY_REQKEY_DEFL_PROCESS_KEYRING 2 +#define KEY_REQKEY_DEFL_SESSION_KEYRING 3 +#define KEY_REQKEY_DEFL_USER_KEYRING 4 +#define KEY_REQKEY_DEFL_USER_SESSION_KEYRING 5 +#define KEY_REQKEY_DEFL_GROUP_KEYRING 6 + /* keyctl commands */ #define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */ #define KEYCTL_JOIN_SESSION_KEYRING 1 /* join or start named session keyring */ @@ -35,5 +45,6 @@ #define KEYCTL_READ 11 /* read a key or keyring's contents */ #define KEYCTL_INSTANTIATE 12 /* instantiate a partially constructed key */ #define KEYCTL_NEGATE 13 /* negate a partially constructed key */ +#define KEYCTL_SET_REQKEY_KEYRING 14 /* set default request-key keyring */ #endif /* _LINUX_KEYCTL_H */ diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 95d0e4b0814d..e4a231549407 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -34,7 +35,17 @@ static inline int request_module(const char * name, ...) { return -ENOSYS; } #endif #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) -extern int call_usermodehelper(char *path, char *argv[], char *envp[], int wait); + +struct key; +extern int call_usermodehelper_keys(char *path, char *argv[], char *envp[], + struct key *session_keyring, int wait); + +static inline int +call_usermodehelper(char *path, char **argv, char **envp, int wait) +{ + return call_usermodehelper_keys(path, argv, envp, NULL, wait); +} + extern void usermodehelper_init(void); #endif /* __LINUX_KMOD_H__ */ diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 99ddba5a4e00..5e1a7b0d7b3f 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -25,27 +25,45 @@ * Rusty Russell). * 2004-July Suparna Bhattacharya added jumper probes * interface to access function arguments. + * 2005-May Hien Nguyen and Jim Keniston + * and Prasanna S Panchamukhi + * added function-return probes. */ #include #include #include #include + #include +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 +#define KPROBE_REENTER 0x00000004 +#define KPROBE_HIT_SSDONE 0x00000008 + struct kprobe; struct pt_regs; +struct kretprobe; +struct kretprobe_instance; typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *); typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *); typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *, unsigned long flags); typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *, int trapnr); +typedef int (*kretprobe_handler_t) (struct kretprobe_instance *, + struct pt_regs *); + struct kprobe { struct hlist_node hlist; /* list of kprobes for multi-handler support */ struct list_head list; + /*count the number of times this probe was temporarily disarmed */ + unsigned long nmissed; + /* location of the probe point */ kprobe_opcode_t *addr; @@ -85,6 +103,62 @@ struct jprobe { kprobe_opcode_t *entry; /* probe handling code to jump to */ }; +#ifdef ARCH_SUPPORTS_KRETPROBES +extern int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs); +extern void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags); +extern struct task_struct *arch_get_kprobe_task(void *ptr); +extern void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs); +extern void arch_kprobe_flush_task(struct task_struct *tk); +#else /* ARCH_SUPPORTS_KRETPROBES */ +static inline void kretprobe_trampoline(void) +{ +} +static inline int trampoline_probe_handler(struct kprobe *p, + struct pt_regs *regs) +{ + return 0; +} +static inline void trampoline_post_handler(struct kprobe *p, + struct pt_regs *regs, unsigned long flags) +{ +} +static inline void arch_prepare_kretprobe(struct kretprobe *rp, + struct pt_regs *regs) +{ +} +static inline void arch_kprobe_flush_task(struct task_struct *tk) +{ +} +#define arch_get_kprobe_task(ptr) ((struct task_struct *)NULL) +#endif /* ARCH_SUPPORTS_KRETPROBES */ +/* + * Function-return probe - + * Note: + * User needs to provide a handler function, and initialize maxactive. + * maxactive - The maximum number of instances of the probed function that + * can be active concurrently. + * nmissed - tracks the number of times the probed function's return was + * ignored, due to maxactive being too low. + * + */ +struct kretprobe { + struct kprobe kp; + kretprobe_handler_t handler; + int maxactive; + int nmissed; + struct hlist_head free_instances; + struct hlist_head used_instances; +}; + +struct kretprobe_instance { + struct hlist_node uflist; /* either on free list or used list */ + struct hlist_node hlist; + struct kretprobe *rp; + void *ret_addr; + void *stack_addr; +}; + #ifdef CONFIG_KPROBES /* Locks kprobe: irq must be disabled */ void lock_kprobes(void); @@ -99,11 +173,14 @@ static inline int kprobe_running(void) extern int arch_prepare_kprobe(struct kprobe *p); extern void arch_copy_kprobe(struct kprobe *p); +extern void arch_arm_kprobe(struct kprobe *p); +extern void arch_disarm_kprobe(struct kprobe *p); extern void arch_remove_kprobe(struct kprobe *p); extern void show_registers(struct pt_regs *regs); /* Get the kprobe at this addr (if any). Must have called lock_kprobes */ struct kprobe *get_kprobe(void *addr); +struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk); int register_kprobe(struct kprobe *p); void unregister_kprobe(struct kprobe *p); @@ -113,7 +190,16 @@ int register_jprobe(struct jprobe *p); void unregister_jprobe(struct jprobe *p); void jprobe_return(void); -#else +int register_kretprobe(struct kretprobe *rp); +void unregister_kretprobe(struct kretprobe *rp); + +struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp); +struct kretprobe_instance *get_rp_inst(void *sara); +struct kretprobe_instance *get_rp_inst_tsk(struct task_struct *tk); +void add_rp_inst(struct kretprobe_instance *ri); +void kprobe_flush_task(struct task_struct *tk); +void recycle_rp_inst(struct kretprobe_instance *ri); +#else /* CONFIG_KPROBES */ static inline int kprobe_running(void) { return 0; @@ -135,5 +221,15 @@ static inline void unregister_jprobe(struct jprobe *p) static inline void jprobe_return(void) { } -#endif +static inline int register_kretprobe(struct kretprobe *rp) +{ + return -ENOSYS; +} +static inline void unregister_kretprobe(struct kretprobe *rp) +{ +} +static inline void kprobe_flush_task(struct task_struct *tk) +{ +} +#endif /* CONFIG_KPROBES */ #endif /* _LINUX_KPROBES_H */ diff --git a/include/linux/loop.h b/include/linux/loop.h index 8220d9c9da00..53fa51595443 100644 --- a/include/linux/loop.h +++ b/include/linux/loop.h @@ -61,7 +61,7 @@ struct loop_device { struct semaphore lo_sem; struct semaphore lo_ctl_mutex; struct semaphore lo_bh_mutex; - atomic_t lo_pending; + int lo_pending; request_queue_t *lo_queue; }; diff --git a/include/linux/mempool.h b/include/linux/mempool.h index 4a36edf1c974..796220ce47cc 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -20,9 +20,14 @@ typedef struct mempool_s { mempool_free_t *free; wait_queue_head_t wait; } mempool_t; -extern mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data); -extern int mempool_resize(mempool_t *pool, int new_min_nr, unsigned int __nocast gfp_mask); + +extern mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, + mempool_free_t *free_fn, void *pool_data); +extern mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, + mempool_free_t *free_fn, void *pool_data, int nid); + +extern int mempool_resize(mempool_t *pool, int new_min_nr, + unsigned int __nocast gfp_mask); extern void mempool_destroy(mempool_t *pool); extern void * mempool_alloc(mempool_t *pool, unsigned int __nocast gfp_mask); extern void mempool_free(void *element, mempool_t *pool); diff --git a/include/linux/mm.h b/include/linux/mm.h index 1813b162b0a8..6eb7f48317f8 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -395,19 +395,81 @@ static inline void put_page(struct page *page) /* * The zone field is never updated after free_area_init_core() * sets it, so none of the operations on it need to be atomic. - * We'll have up to (MAX_NUMNODES * MAX_NR_ZONES) zones total, - * so we use (MAX_NODES_SHIFT + MAX_ZONES_SHIFT) here to get enough bits. */ -#define NODEZONE_SHIFT (sizeof(page_flags_t)*8 - MAX_NODES_SHIFT - MAX_ZONES_SHIFT) -#define NODEZONE(node, zone) ((node << ZONES_SHIFT) | zone) + + +/* + * page->flags layout: + * + * There are three possibilities for how page->flags get + * laid out. The first is for the normal case, without + * sparsemem. The second is for sparsemem when there is + * plenty of space for node and section. The last is when + * we have run out of space and have to fall back to an + * alternate (slower) way of determining the node. + * + * No sparsemem: | NODE | ZONE | ... | FLAGS | + * with space for node: | SECTION | NODE | ZONE | ... | FLAGS | + * no space for node: | SECTION | ZONE | ... | FLAGS | + */ +#ifdef CONFIG_SPARSEMEM +#define SECTIONS_WIDTH SECTIONS_SHIFT +#else +#define SECTIONS_WIDTH 0 +#endif + +#define ZONES_WIDTH ZONES_SHIFT + +#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT <= FLAGS_RESERVED +#define NODES_WIDTH NODES_SHIFT +#else +#define NODES_WIDTH 0 +#endif + +/* Page flags: | [SECTION] | [NODE] | ZONE | ... | FLAGS | */ +#define SECTIONS_PGOFF ((sizeof(page_flags_t)*8) - SECTIONS_WIDTH) +#define NODES_PGOFF (SECTIONS_PGOFF - NODES_WIDTH) +#define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH) + +/* + * We are going to use the flags for the page to node mapping if its in + * there. This includes the case where there is no node, so it is implicit. + */ +#define FLAGS_HAS_NODE (NODES_WIDTH > 0 || NODES_SHIFT == 0) + +#ifndef PFN_SECTION_SHIFT +#define PFN_SECTION_SHIFT 0 +#endif + +/* + * Define the bit shifts to access each section. For non-existant + * sections we define the shift as 0; that plus a 0 mask ensures + * the compiler will optimise away reference to them. + */ +#define SECTIONS_PGSHIFT (SECTIONS_PGOFF * (SECTIONS_WIDTH != 0)) +#define NODES_PGSHIFT (NODES_PGOFF * (NODES_WIDTH != 0)) +#define ZONES_PGSHIFT (ZONES_PGOFF * (ZONES_WIDTH != 0)) + +/* NODE:ZONE or SECTION:ZONE is used to lookup the zone from a page. */ +#if FLAGS_HAS_NODE +#define ZONETABLE_SHIFT (NODES_SHIFT + ZONES_SHIFT) +#else +#define ZONETABLE_SHIFT (SECTIONS_SHIFT + ZONES_SHIFT) +#endif +#define ZONETABLE_PGSHIFT ZONES_PGSHIFT + +#if SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH > FLAGS_RESERVED +#error SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH > FLAGS_RESERVED +#endif + +#define ZONES_MASK ((1UL << ZONES_WIDTH) - 1) +#define NODES_MASK ((1UL << NODES_WIDTH) - 1) +#define SECTIONS_MASK ((1UL << SECTIONS_WIDTH) - 1) +#define ZONETABLE_MASK ((1UL << ZONETABLE_SHIFT) - 1) static inline unsigned long page_zonenum(struct page *page) { - return (page->flags >> NODEZONE_SHIFT) & (~(~0UL << ZONES_SHIFT)); -} -static inline unsigned long page_to_nid(struct page *page) -{ - return (page->flags >> (NODEZONE_SHIFT + ZONES_SHIFT)); + return (page->flags >> ZONES_PGSHIFT) & ZONES_MASK; } struct zone; @@ -415,13 +477,44 @@ extern struct zone *zone_table[]; static inline struct zone *page_zone(struct page *page) { - return zone_table[page->flags >> NODEZONE_SHIFT]; + return zone_table[(page->flags >> ZONETABLE_PGSHIFT) & + ZONETABLE_MASK]; } -static inline void set_page_zone(struct page *page, unsigned long nodezone_num) +static inline unsigned long page_to_nid(struct page *page) { - page->flags &= ~(~0UL << NODEZONE_SHIFT); - page->flags |= nodezone_num << NODEZONE_SHIFT; + if (FLAGS_HAS_NODE) + return (page->flags >> NODES_PGSHIFT) & NODES_MASK; + else + return page_zone(page)->zone_pgdat->node_id; +} +static inline unsigned long page_to_section(struct page *page) +{ + return (page->flags >> SECTIONS_PGSHIFT) & SECTIONS_MASK; +} + +static inline void set_page_zone(struct page *page, unsigned long zone) +{ + page->flags &= ~(ZONES_MASK << ZONES_PGSHIFT); + page->flags |= (zone & ZONES_MASK) << ZONES_PGSHIFT; +} +static inline void set_page_node(struct page *page, unsigned long node) +{ + page->flags &= ~(NODES_MASK << NODES_PGSHIFT); + page->flags |= (node & NODES_MASK) << NODES_PGSHIFT; +} +static inline void set_page_section(struct page *page, unsigned long section) +{ + page->flags &= ~(SECTIONS_MASK << SECTIONS_PGSHIFT); + page->flags |= (section & SECTIONS_MASK) << SECTIONS_PGSHIFT; +} + +static inline void set_page_links(struct page *page, unsigned long zone, + unsigned long node, unsigned long pfn) +{ + set_page_zone(page, zone); + set_page_node(page, node); + set_page_section(page, pfn_to_section_nr(pfn)); } #ifndef CONFIG_DISCONTIGMEM diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 4733d35d8223..6c90461ed99f 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -269,7 +269,9 @@ typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; struct zonelist node_zonelists[GFP_ZONETYPES]; int nr_zones; +#ifdef CONFIG_FLAT_NODE_MEM_MAP struct page *node_mem_map; +#endif struct bootmem_data *bdata; unsigned long node_start_pfn; unsigned long node_present_pages; /* total number of physical pages */ @@ -284,6 +286,12 @@ typedef struct pglist_data { #define node_present_pages(nid) (NODE_DATA(nid)->node_present_pages) #define node_spanned_pages(nid) (NODE_DATA(nid)->node_spanned_pages) +#ifdef CONFIG_FLAT_NODE_MEM_MAP +#define pgdat_page_nr(pgdat, pagenr) ((pgdat)->node_mem_map + (pagenr)) +#else +#define pgdat_page_nr(pgdat, pagenr) pfn_to_page((pgdat)->node_start_pfn + (pagenr)) +#endif +#define nid_page_nr(nid, pagenr) pgdat_page_nr(NODE_DATA(nid),(pagenr)) extern struct pglist_data *pgdat_list; @@ -400,7 +408,7 @@ int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, struct file *, /* Returns the number of the current Node. */ #define numa_node_id() (cpu_to_node(raw_smp_processor_id())) -#ifndef CONFIG_DISCONTIGMEM +#ifndef CONFIG_NEED_MULTIPLE_NODES extern struct pglist_data contig_page_data; #define NODE_DATA(nid) (&contig_page_data) @@ -408,36 +416,177 @@ extern struct pglist_data contig_page_data; #define MAX_NODES_SHIFT 1 #define pfn_to_nid(pfn) (0) -#else /* CONFIG_DISCONTIGMEM */ +#else /* CONFIG_NEED_MULTIPLE_NODES */ #include +#endif /* !CONFIG_NEED_MULTIPLE_NODES */ + +#ifdef CONFIG_SPARSEMEM +#include +#endif + #if BITS_PER_LONG == 32 || defined(ARCH_HAS_ATOMIC_UNSIGNED) /* * with 32 bit page->flags field, we reserve 8 bits for node/zone info. * there are 3 zones (2 bits) and this leaves 8-2=6 bits for nodes. */ -#define MAX_NODES_SHIFT 6 +#define FLAGS_RESERVED 8 + #elif BITS_PER_LONG == 64 /* * with 64 bit flags field, there's plenty of room. */ -#define MAX_NODES_SHIFT 10 +#define FLAGS_RESERVED 32 + +#else + +#error BITS_PER_LONG not defined + #endif -#endif /* !CONFIG_DISCONTIGMEM */ - -#if NODES_SHIFT > MAX_NODES_SHIFT -#error NODES_SHIFT > MAX_NODES_SHIFT +#ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID +#define early_pfn_to_nid(nid) (0UL) #endif -/* There are currently 3 zones: DMA, Normal & Highmem, thus we need 2 bits */ -#define MAX_ZONES_SHIFT 2 +#define pfn_to_section_nr(pfn) ((pfn) >> PFN_SECTION_SHIFT) +#define section_nr_to_pfn(sec) ((sec) << PFN_SECTION_SHIFT) -#if ZONES_SHIFT > MAX_ZONES_SHIFT -#error ZONES_SHIFT > MAX_ZONES_SHIFT +#ifdef CONFIG_SPARSEMEM + +/* + * SECTION_SHIFT #bits space required to store a section # + * + * PA_SECTION_SHIFT physical address to/from section number + * PFN_SECTION_SHIFT pfn to/from section number + */ +#define SECTIONS_SHIFT (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS) + +#define PA_SECTION_SHIFT (SECTION_SIZE_BITS) +#define PFN_SECTION_SHIFT (SECTION_SIZE_BITS - PAGE_SHIFT) + +#define NR_MEM_SECTIONS (1UL << SECTIONS_SHIFT) + +#define PAGES_PER_SECTION (1UL << PFN_SECTION_SHIFT) +#define PAGE_SECTION_MASK (~(PAGES_PER_SECTION-1)) + +#if (MAX_ORDER - 1 + PAGE_SHIFT) > SECTION_SIZE_BITS +#error Allocator MAX_ORDER exceeds SECTION_SIZE #endif +struct page; +struct mem_section { + /* + * This is, logically, a pointer to an array of struct + * pages. However, it is stored with some other magic. + * (see sparse.c::sparse_init_one_section()) + * + * Making it a UL at least makes someone do a cast + * before using it wrong. + */ + unsigned long section_mem_map; +}; + +extern struct mem_section mem_section[NR_MEM_SECTIONS]; + +static inline struct mem_section *__nr_to_section(unsigned long nr) +{ + return &mem_section[nr]; +} + +/* + * We use the lower bits of the mem_map pointer to store + * a little bit of information. There should be at least + * 3 bits here due to 32-bit alignment. + */ +#define SECTION_MARKED_PRESENT (1UL<<0) +#define SECTION_HAS_MEM_MAP (1UL<<1) +#define SECTION_MAP_LAST_BIT (1UL<<2) +#define SECTION_MAP_MASK (~(SECTION_MAP_LAST_BIT-1)) + +static inline struct page *__section_mem_map_addr(struct mem_section *section) +{ + unsigned long map = section->section_mem_map; + map &= SECTION_MAP_MASK; + return (struct page *)map; +} + +static inline int valid_section(struct mem_section *section) +{ + return (section->section_mem_map & SECTION_MARKED_PRESENT); +} + +static inline int section_has_mem_map(struct mem_section *section) +{ + return (section->section_mem_map & SECTION_HAS_MEM_MAP); +} + +static inline int valid_section_nr(unsigned long nr) +{ + return valid_section(__nr_to_section(nr)); +} + +/* + * Given a kernel address, find the home node of the underlying memory. + */ +#define kvaddr_to_nid(kaddr) pfn_to_nid(__pa(kaddr) >> PAGE_SHIFT) + +static inline struct mem_section *__pfn_to_section(unsigned long pfn) +{ + return __nr_to_section(pfn_to_section_nr(pfn)); +} + +#define pfn_to_page(pfn) \ +({ \ + unsigned long __pfn = (pfn); \ + __section_mem_map_addr(__pfn_to_section(__pfn)) + __pfn; \ +}) +#define page_to_pfn(page) \ +({ \ + page - __section_mem_map_addr(__nr_to_section( \ + page_to_section(page))); \ +}) + +static inline int pfn_valid(unsigned long pfn) +{ + if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) + return 0; + return valid_section(__nr_to_section(pfn_to_section_nr(pfn))); +} + +/* + * These are _only_ used during initialisation, therefore they + * can use __initdata ... They could have names to indicate + * this restriction. + */ +#ifdef CONFIG_NUMA +#define pfn_to_nid early_pfn_to_nid +#endif + +#define pfn_to_pgdat(pfn) \ +({ \ + NODE_DATA(pfn_to_nid(pfn)); \ +}) + +#define early_pfn_valid(pfn) pfn_valid(pfn) +void sparse_init(void); +#else +#define sparse_init() do {} while (0) +#endif /* CONFIG_SPARSEMEM */ + +#ifdef CONFIG_NODES_SPAN_OTHER_NODES +#define early_pfn_in_nid(pfn, nid) (early_pfn_to_nid(pfn) == (nid)) +#else +#define early_pfn_in_nid(pfn, nid) (1) +#endif + +#ifndef early_pfn_valid +#define early_pfn_valid(pfn) (1) +#endif + +void memory_present(int nid, unsigned long start, unsigned long end); +unsigned long __init node_memmap_size_bytes(int, unsigned long, unsigned long); + #endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _LINUX_MMZONE_H */ diff --git a/include/linux/module.h b/include/linux/module.h index 0e432a0f4aee..f05372b7fe77 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -51,6 +51,9 @@ struct module_attribute { ssize_t (*show)(struct module_attribute *, struct module *, char *); ssize_t (*store)(struct module_attribute *, struct module *, const char *, size_t count); + void (*setup)(struct module *, const char *); + int (*test)(struct module *); + void (*free)(struct module *); }; struct module_kobject @@ -239,6 +242,8 @@ struct module /* Sysfs stuff. */ struct module_kobject mkobj; struct module_param_attrs *param_attrs; + const char *version; + const char *srcversion; /* Exported symbols */ const struct kernel_symbol *syms; diff --git a/include/linux/namespace.h b/include/linux/namespace.h index 9eca1558d72f..697991b69f9b 100644 --- a/include/linux/namespace.h +++ b/include/linux/namespace.h @@ -12,7 +12,6 @@ struct namespace { struct rw_semaphore sem; }; -extern void umount_tree(struct vfsmount *); extern int copy_namespace(int, struct task_struct *); extern void __put_namespace(struct namespace *namespace); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d6afd440cf7b..3a0ed7f9e801 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -164,12 +164,6 @@ struct netif_rx_stats unsigned total; unsigned dropped; unsigned time_squeeze; - unsigned throttled; - unsigned fastroute_hit; - unsigned fastroute_success; - unsigned fastroute_defer; - unsigned fastroute_deferred_out; - unsigned fastroute_latency_reduction; unsigned cpu_collision; }; @@ -562,12 +556,9 @@ static inline int unregister_gifconf(unsigned int family) struct softnet_data { - int throttle; - int cng_level; - int avg_blog; + struct net_device *output_queue; struct sk_buff_head input_pkt_queue; struct list_head poll_list; - struct net_device *output_queue; struct sk_buff *completion_queue; struct net_device backlog_dev; /* Sorry. 8) */ @@ -925,10 +916,6 @@ extern int skb_checksum_help(struct sk_buff *skb, int inward); extern void net_enable_timestamp(void); extern void net_disable_timestamp(void); -#ifdef CONFIG_SYSCTL -extern char *net_sysctl_strdup(const char *s); -#endif - #endif /* __KERNEL__ */ #endif /* _LINUX_DEV_H */ diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 5bb5b2fd7ba2..0c1c306cdaec 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -28,7 +28,7 @@ #define NFS4_ACCESS_DELETE 0x0010 #define NFS4_ACCESS_EXECUTE 0x0020 -#define NFS4_FH_PERISTENT 0x0000 +#define NFS4_FH_PERSISTENT 0x0000 #define NFS4_FH_NOEXPIRE_WITH_OPEN 0x0001 #define NFS4_FH_VOLATILE_ANY 0x0002 #define NFS4_FH_VOL_MIGRATION 0x0004 diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 4bf931d5ff56..5791dfd30dd0 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -145,15 +145,19 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *); * NFSv4 State */ #ifdef CONFIG_NFSD_V4 -int nfs4_state_init(void); +void nfs4_state_init(void); +int nfs4_state_start(void); void nfs4_state_shutdown(void); time_t nfs4_lease_time(void); void nfs4_reset_lease(time_t leasetime); +int nfs4_reset_recoverydir(char *recdir); #else -static inline int nfs4_state_init(void){return 0;} +static inline void nfs4_state_init(void){}; +static inline int nfs4_state_start(void){return 0;} static inline void nfs4_state_shutdown(void){} static inline time_t nfs4_lease_time(void){return 0;} static inline void nfs4_reset_lease(time_t leasetime){} +static inline int nfs4_reset_recoverydir(char *recdir) {return 0;} #endif /* diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index b6b2fe1e7c63..a84a3fa99be1 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -61,11 +61,6 @@ typedef struct { #define si_stateownerid si_opaque.so_stateownerid #define si_fileid si_opaque.so_fileid -extern stateid_t zerostateid; -extern stateid_t onestateid; - -#define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) -#define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) struct nfs4_cb_recall { u32 cbr_ident; @@ -77,8 +72,8 @@ struct nfs4_cb_recall { }; struct nfs4_delegation { - struct list_head dl_del_perfile; /* nfs4_file->fi_del_perfile */ - struct list_head dl_del_perclnt; /* nfs4_client->cl_del_perclnt*/ + struct list_head dl_perfile; + struct list_head dl_perclnt; struct list_head dl_recall_lru; /* delegation recalled */ atomic_t dl_count; /* ref count */ struct nfs4_client *dl_client; @@ -97,7 +92,6 @@ struct nfs4_delegation { /* client delegation callback info */ struct nfs4_callback { /* SETCLIENTID info */ - u32 cb_parsed; /* addr parsed */ u32 cb_addr; unsigned short cb_port; u32 cb_prog; @@ -109,6 +103,8 @@ struct nfs4_callback { struct rpc_clnt * cb_client; }; +#define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */ + /* * struct nfs4_client - one per client. Clientids live here. * o Each nfs4_client is hashed by clientid. @@ -122,10 +118,11 @@ struct nfs4_callback { struct nfs4_client { struct list_head cl_idhash; /* hash by cl_clientid.id */ struct list_head cl_strhash; /* hash by cl_name */ - struct list_head cl_perclient; /* list: stateowners */ - struct list_head cl_del_perclnt; /* list: delegations */ + struct list_head cl_openowners; + struct list_head cl_delegations; struct list_head cl_lru; /* tail queue */ struct xdr_netobj cl_name; /* id generated by client */ + char cl_recdir[HEXDIR_LEN]; /* recovery dir */ nfs4_verifier cl_verifier; /* generated by client */ time_t cl_time; /* time of last lease renewal */ u32 cl_addr; /* client ipaddress */ @@ -134,6 +131,7 @@ struct nfs4_client { nfs4_verifier cl_confirm; /* generated by server */ struct nfs4_callback cl_callback; /* callback info */ atomic_t cl_count; /* ref count */ + u32 cl_firststate; /* recovery dir creation */ }; /* struct nfs4_client_reset @@ -143,7 +141,7 @@ struct nfs4_client { */ struct nfs4_client_reclaim { struct list_head cr_strhash; /* hash by cr_name */ - struct xdr_netobj cr_name; /* id generated by client */ + char cr_recdir[HEXDIR_LEN]; /* recover dir */ }; static inline void @@ -197,9 +195,9 @@ struct nfs4_stateowner { struct kref so_ref; struct list_head so_idhash; /* hash by so_id */ struct list_head so_strhash; /* hash by op_name */ - struct list_head so_perclient; /* nfs4_client->cl_perclient */ - struct list_head so_perfilestate; /* list: nfs4_stateid */ - struct list_head so_perlockowner; /* nfs4_stateid->st_perlockowner */ + struct list_head so_perclient; + struct list_head so_stateids; + struct list_head so_perstateid; /* for lockowners only */ struct list_head so_close_lru; /* tail queue */ time_t so_time; /* time of placement on so_close_lru */ int so_is_open_owner; /* 1=openowner,0=lockowner */ @@ -217,9 +215,10 @@ struct nfs4_stateowner { * share_acces, share_deny on the file. */ struct nfs4_file { + struct kref fi_ref; struct list_head fi_hash; /* hash by "struct inode *" */ - struct list_head fi_perfile; /* list: nfs4_stateid */ - struct list_head fi_del_perfile; /* list: nfs4_delegation */ + struct list_head fi_stateids; + struct list_head fi_delegations; struct inode *fi_inode; u32 fi_id; /* used with stateowner->so_id * for stateid_hashtbl hash */ @@ -241,8 +240,8 @@ struct nfs4_file { struct nfs4_stateid { struct list_head st_hash; struct list_head st_perfile; - struct list_head st_perfilestate; - struct list_head st_perlockowner; + struct list_head st_perstateowner; + struct list_head st_lockowners; struct nfs4_stateowner * st_stateowner; struct nfs4_file * st_file; stateid_t st_stateid; @@ -267,12 +266,9 @@ struct nfs4_stateid { ((err) != nfserr_stale_stateid) && \ ((err) != nfserr_bad_stateid)) -extern time_t nfs4_laundromat(void); extern int nfsd4_renew(clientid_t *clid); extern int nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int flags, struct file **filp); -extern int nfs4_share_conflict(struct svc_fh *current_fh, - unsigned int deny_type); extern void nfs4_lock_state(void); extern void nfs4_unlock_state(void); extern int nfs4_in_grace(void); @@ -282,6 +278,15 @@ extern void nfs4_free_stateowner(struct kref *kref); extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_cb_recall(struct nfs4_delegation *dp); extern void nfs4_put_delegation(struct nfs4_delegation *dp); +extern int nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); +extern void nfsd4_init_recdir(char *recdir_name); +extern int nfsd4_recdir_load(void); +extern void nfsd4_shutdown_recdir(void); +extern int nfs4_client_to_reclaim(const char *name); +extern int nfs4_has_reclaimed_state(const char *name); +extern void nfsd4_recdir_purge_old(void); +extern int nfsd4_create_clid_dir(struct nfs4_client *clp); +extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); static inline void nfs4_put_stateowner(struct nfs4_stateowner *so) diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index a1f5ad0be1bf..4d24d65c0e88 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -210,6 +210,7 @@ struct nfsd4_open { u32 op_share_access; /* request */ u32 op_share_deny; /* request */ stateid_t op_stateid; /* response */ + u32 op_recall; /* recall */ struct nfsd4_change_info op_cinfo; /* response */ u32 op_rflags; /* response */ int op_truncate; /* used during processing */ diff --git a/include/linux/nfsd_idmap.h b/include/linux/nfsd_idmap.h index 9bb7f30e923b..e82746fcad14 100644 --- a/include/linux/nfsd_idmap.h +++ b/include/linux/nfsd_idmap.h @@ -43,8 +43,13 @@ /* XXX from linux/nfs_idmap.h */ #define IDMAP_NAMESZ 128 +#ifdef CONFIG_NFSD_V4 void nfsd_idmap_init(void); void nfsd_idmap_shutdown(void); +#else +static inline void nfsd_idmap_init(void) {}; +static inline void nfsd_idmap_shutdown(void) {}; +#endif int nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, __u32 *); int nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, __u32 *); diff --git a/include/linux/numa.h b/include/linux/numa.h index bd0c8c4e9a95..f0c539bd3cfc 100644 --- a/include/linux/numa.h +++ b/include/linux/numa.h @@ -3,7 +3,7 @@ #include -#ifdef CONFIG_DISCONTIGMEM +#ifndef CONFIG_FLATMEM #include #endif diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 63e89e47b8e9..bf608808a60c 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1568,6 +1568,7 @@ #define PCI_DEVICE_ID_SERVERWORKS_OSB4USB 0x0220 #define PCI_DEVICE_ID_SERVERWORKS_CSB5USB PCI_DEVICE_ID_SERVERWORKS_OSB4USB #define PCI_DEVICE_ID_SERVERWORKS_CSB6USB 0x0221 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 #define PCI_DEVICE_ID_SERVERWORKS_GCLE 0x0225 #define PCI_DEVICE_ID_SERVERWORKS_GCLE2 0x0227 #define PCI_DEVICE_ID_SERVERWORKS_CSB5ISA 0x0230 diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index d2aa214d6803..25d2d67c1faf 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -408,6 +408,7 @@ enum TCF_EM_NBYTE, TCF_EM_U32, TCF_EM_META, + TCF_EM_TEXT, __TCF_EM_MAX }; diff --git a/include/linux/pktcdvd.h b/include/linux/pktcdvd.h index 4e2d2a942ecb..4b32bce9a289 100644 --- a/include/linux/pktcdvd.h +++ b/include/linux/pktcdvd.h @@ -159,7 +159,7 @@ struct packet_iosched struct bio *read_queue_tail; struct bio *write_queue; struct bio *write_queue_tail; - int high_prio_read; /* An important read request has been queued */ + sector_t last_write; /* The sector where the last write ended */ int successive_reads; }; diff --git a/include/linux/posix_acl_xattr.h b/include/linux/posix_acl_xattr.h index 5efd0a6dad94..6e53c34035cd 100644 --- a/include/linux/posix_acl_xattr.h +++ b/include/linux/posix_acl_xattr.h @@ -23,13 +23,13 @@ #define ACL_UNDEFINED_ID (-1) typedef struct { - __u16 e_tag; - __u16 e_perm; - __u32 e_id; + __le16 e_tag; + __le16 e_perm; + __le32 e_id; } posix_acl_xattr_entry; typedef struct { - __u32 a_version; + __le32 a_version; posix_acl_xattr_entry a_entries[0]; } posix_acl_xattr_header; @@ -52,4 +52,7 @@ posix_acl_xattr_count(size_t size) return size / sizeof(posix_acl_xattr_entry); } +struct posix_acl *posix_acl_from_xattr(const void *value, size_t size); +int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size); + #endif /* _POSIX_ACL_XATTR_H */ diff --git a/include/linux/qnx4_fs.h b/include/linux/qnx4_fs.h index 22ba580b0ae8..fc610bb0f733 100644 --- a/include/linux/qnx4_fs.h +++ b/include/linux/qnx4_fs.h @@ -46,11 +46,11 @@ struct qnx4_inode_entry { char di_fname[QNX4_SHORT_NAME_MAX]; qnx4_off_t di_size; qnx4_xtnt_t di_first_xtnt; - __u32 di_xblk; - __s32 di_ftime; - __s32 di_mtime; - __s32 di_atime; - __s32 di_ctime; + __le32 di_xblk; + __le32 di_ftime; + __le32 di_mtime; + __le32 di_atime; + __le32 di_ctime; qnx4_nxtnt_t di_num_xtnts; qnx4_mode_t di_mode; qnx4_muid_t di_uid; @@ -63,18 +63,18 @@ struct qnx4_inode_entry { struct qnx4_link_info { char dl_fname[QNX4_NAME_MAX]; - __u32 dl_inode_blk; + __le32 dl_inode_blk; __u8 dl_inode_ndx; __u8 dl_spare[10]; __u8 dl_status; }; struct qnx4_xblk { - __u32 xblk_next_xblk; - __u32 xblk_prev_xblk; + __le32 xblk_next_xblk; + __le32 xblk_prev_xblk; __u8 xblk_num_xtnts; __u8 xblk_spare[3]; - __s32 xblk_num_blocks; + __le32 xblk_num_blocks; qnx4_xtnt_t xblk_xtnts[QNX4_MAX_XTNTS_PER_XBLK]; char xblk_signature[8]; qnx4_xtnt_t xblk_first_xtnt; diff --git a/include/linux/qnxtypes.h b/include/linux/qnxtypes.h index fb518e318c7c..a3eb1137857b 100644 --- a/include/linux/qnxtypes.h +++ b/include/linux/qnxtypes.h @@ -12,18 +12,18 @@ #ifndef _QNX4TYPES_H #define _QNX4TYPES_H -typedef __u16 qnx4_nxtnt_t; +typedef __le16 qnx4_nxtnt_t; typedef __u8 qnx4_ftype_t; typedef struct { - __u32 xtnt_blk; - __u32 xtnt_size; + __le32 xtnt_blk; + __le32 xtnt_size; } qnx4_xtnt_t; -typedef __u16 qnx4_mode_t; -typedef __u16 qnx4_muid_t; -typedef __u16 qnx4_mgid_t; -typedef __u32 qnx4_off_t; -typedef __u16 qnx4_nlink_t; +typedef __le16 qnx4_mode_t; +typedef __le16 qnx4_muid_t; +typedef __le16 qnx4_mgid_t; +typedef __le32 qnx4_off_t; +typedef __le16 qnx4_nlink_t; #endif diff --git a/include/linux/quota.h b/include/linux/quota.h index ac5b90f4f256..700ead45084f 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -138,8 +138,11 @@ struct if_dqinfo { #include /* Maximal numbers of writes for quota operation (insert/delete/update) - * (over all formats) - info block, 4 pointer blocks, data block */ -#define DQUOT_MAX_WRITES 6 + * (over VFS all formats) */ +#define DQUOT_INIT_ALLOC max(V1_INIT_ALLOC, V2_INIT_ALLOC) +#define DQUOT_INIT_REWRITE max(V1_INIT_REWRITE, V2_INIT_REWRITE) +#define DQUOT_DEL_ALLOC max(V1_DEL_ALLOC, V2_DEL_ALLOC) +#define DQUOT_DEL_REWRITE max(V1_DEL_REWRITE, V2_DEL_REWRITE) /* * Data for one user/group kept in memory diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index e57baa85e744..d211507ab246 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -39,7 +39,8 @@ extern int dquot_commit_info(struct super_block *sb, int type); extern int dquot_mark_dquot_dirty(struct dquot *dquot); extern int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path); -extern int vfs_quota_on_mount(int type, int format_id, struct dentry *dentry); +extern int vfs_quota_on_mount(struct super_block *sb, char *qf_name, + int format_id, int type); extern int vfs_quota_off(struct super_block *sb, int type); #define vfs_quota_off_mount(sb, type) vfs_quota_off(sb, type) extern int vfs_quota_sync(struct super_block *sb, int type); diff --git a/include/linux/reiserfs_acl.h b/include/linux/reiserfs_acl.h index 2aef9c3f5ce8..0760507a545b 100644 --- a/include/linux/reiserfs_acl.h +++ b/include/linux/reiserfs_acl.h @@ -1,6 +1,5 @@ #include #include -#include #define REISERFS_ACL_VERSION 0x0001 diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 32148625fc2f..4c7c5689ad93 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -1644,11 +1644,18 @@ struct reiserfs_journal_header { #define JOURNAL_MAX_TRANS_AGE 30 #define JOURNAL_PER_BALANCE_CNT (3 * (MAX_HEIGHT-2) + 9) #ifdef CONFIG_QUOTA -#define REISERFS_QUOTA_TRANS_BLOCKS 2 /* We need to update data and inode (atime) */ -#define REISERFS_QUOTA_INIT_BLOCKS (DQUOT_MAX_WRITES*(JOURNAL_PER_BALANCE_CNT+2)+1) /* 1 balancing, 1 bitmap, 1 data per write + stat data update */ +/* We need to update data and inode (atime) */ +#define REISERFS_QUOTA_TRANS_BLOCKS(s) (REISERFS_SB(s)->s_mount_opt & (1<s_mount_opt & (1<s_mount_opt & (1<blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK]) @@ -660,6 +661,7 @@ struct task_struct { struct user_struct *user; #ifdef CONFIG_KEYS struct key *thread_keyring; /* keyring private to this thread */ + unsigned char jit_keyring; /* default keyring to attach requested keys to */ #endif int oomkilladj; /* OOM kill score adjustment (bit shift). */ char comm[TASK_COMM_LEN]; /* executable name excluding path diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d7c839a21842..416a2e4024b2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #define HAVE_ALLOC_SKB /* For the drivers to know */ @@ -321,6 +322,28 @@ extern void skb_over_panic(struct sk_buff *skb, int len, extern void skb_under_panic(struct sk_buff *skb, int len, void *here); +struct skb_seq_state +{ + __u32 lower_offset; + __u32 upper_offset; + __u32 frag_idx; + __u32 stepped_offset; + struct sk_buff *root_skb; + struct sk_buff *cur_skb; + __u8 *frag_data; +}; + +extern void skb_prepare_seq_read(struct sk_buff *skb, + unsigned int from, unsigned int to, + struct skb_seq_state *st); +extern unsigned int skb_seq_read(unsigned int consumed, const u8 **data, + struct skb_seq_state *st); +extern void skb_abort_seq_read(struct skb_seq_state *st); + +extern unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, + unsigned int to, struct ts_config *config, + struct ts_state *state); + /* Internal */ #define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) diff --git a/include/linux/string.h b/include/linux/string.h index b9fc59469956..93994c613095 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -88,6 +88,8 @@ extern int memcmp(const void *,const void *,__kernel_size_t); extern void * memchr(const void *,int,__kernel_size_t); #endif +extern char *kstrdup(const char *s, int gfp); + #ifdef __cplusplus } #endif diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index a17745c80a91..ebfe1250f0a4 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -136,6 +136,7 @@ enum KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */ KERN_BOOTLOADER_TYPE=67, /* int: boot loader type */ KERN_RANDOMIZE=68, /* int: randomize virtual address space */ + KERN_SETUID_DUMPABLE=69, /* int: behaviour of dumps for setuid core */ }; @@ -242,6 +243,7 @@ enum NET_CORE_MOD_CONG=16, NET_CORE_DEV_WEIGHT=17, NET_CORE_SOMAXCONN=18, + NET_CORE_BUDGET=19, }; /* /proc/sys/net/ethernet */ @@ -332,21 +334,14 @@ enum NET_TCP_FRTO=92, NET_TCP_LOW_LATENCY=93, NET_IPV4_IPFRAG_SECRET_INTERVAL=94, - NET_TCP_WESTWOOD=95, NET_IPV4_IGMP_MAX_MSF=96, NET_TCP_NO_METRICS_SAVE=97, - NET_TCP_VEGAS=98, - NET_TCP_VEGAS_ALPHA=99, - NET_TCP_VEGAS_BETA=100, - NET_TCP_VEGAS_GAMMA=101, - NET_TCP_BIC=102, - NET_TCP_BIC_FAST_CONVERGENCE=103, - NET_TCP_BIC_LOW_WINDOW=104, NET_TCP_DEFAULT_WIN_SCALE=105, NET_TCP_MODERATE_RCVBUF=106, NET_TCP_TSO_WIN_DIVISOR=107, NET_TCP_BIC_BETA=108, NET_IPV4_ICMP_ERRORS_USE_INBOUND_IFADDR=109, + NET_TCP_CONG_CONTROL=110, }; enum { diff --git a/include/linux/tc_ematch/tc_em_text.h b/include/linux/tc_ematch/tc_em_text.h new file mode 100644 index 000000000000..7cd43e99c7f5 --- /dev/null +++ b/include/linux/tc_ematch/tc_em_text.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_TC_EM_TEXT_H +#define __LINUX_TC_EM_TEXT_H + +#include + +#define TC_EM_TEXT_ALGOSIZ 16 + +struct tcf_em_text +{ + char algo[TC_EM_TEXT_ALGOSIZ]; + __u16 from_offset; + __u16 to_offset; + __u16 pattern_len; + __u8 from_layer:4; + __u8 to_layer:4; + __u8 pad; +}; + +#endif diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 97a7c9e03df5..dfd93d03f5d2 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -127,6 +127,7 @@ enum { #define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ #define TCP_INFO 11 /* Information about this connection. */ #define TCP_QUICKACK 12 /* Block/reenable quick acks */ +#define TCP_CONGESTION 13 /* Congestion control algorithm */ #define TCPI_OPT_TIMESTAMPS 1 #define TCPI_OPT_SACK 2 @@ -203,13 +204,6 @@ struct tcp_sack_block { __u32 end_seq; }; -enum tcp_congestion_algo { - TCP_RENO=0, - TCP_VEGAS, - TCP_WESTWOOD, - TCP_BIC, -}; - struct tcp_options_received { /* PAWS/RTTM data */ long ts_recent_stamp;/* Time we stored ts_recent (for aging) */ @@ -305,7 +299,7 @@ struct tcp_sock { __u8 reordering; /* Packet reordering metric. */ __u8 frto_counter; /* Number of new acks after RTO */ - __u8 adv_cong; /* Using Vegas, Westwood, or BIC */ + __u8 unused; __u8 defer_accept; /* User waits for some data after accept() */ /* RTT measurement */ @@ -401,37 +395,10 @@ struct tcp_sock { __u32 time; } rcvq_space; -/* TCP Westwood structure */ - struct { - __u32 bw_ns_est; /* first bandwidth estimation..not too smoothed 8) */ - __u32 bw_est; /* bandwidth estimate */ - __u32 rtt_win_sx; /* here starts a new evaluation... */ - __u32 bk; - __u32 snd_una; /* used for evaluating the number of acked bytes */ - __u32 cumul_ack; - __u32 accounted; - __u32 rtt; - __u32 rtt_min; /* minimum observed RTT */ - } westwood; - -/* Vegas variables */ - struct { - __u32 beg_snd_nxt; /* right edge during last RTT */ - __u32 beg_snd_una; /* left edge during last RTT */ - __u32 beg_snd_cwnd; /* saves the size of the cwnd */ - __u8 doing_vegas_now;/* if true, do vegas for this RTT */ - __u16 cntRTT; /* # of RTTs measured within last RTT */ - __u32 minRTT; /* min of RTTs measured within last RTT (in usec) */ - __u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */ - } vegas; - - /* BI TCP Parameters */ - struct { - __u32 cnt; /* increase cwnd by 1 after this number of ACKs */ - __u32 last_max_cwnd; /* last maximium snd_cwnd */ - __u32 last_cwnd; /* the last snd_cwnd */ - __u32 last_stamp; /* time when updated last_cwnd */ - } bictcp; + /* Pluggable TCP congestion control hook */ + struct tcp_congestion_ops *ca_ops; + u32 ca_priv[16]; +#define TCP_CA_PRIV_SIZE (16*sizeof(u32)) }; static inline struct tcp_sock *tcp_sk(const struct sock *sk) @@ -439,6 +406,11 @@ static inline struct tcp_sock *tcp_sk(const struct sock *sk) return (struct tcp_sock *)sk; } +static inline void *tcp_ca(const struct tcp_sock *tp) +{ + return (void *) tp->ca_priv; +} + #endif #endif /* _LINUX_TCP_H */ diff --git a/include/linux/tcp_diag.h b/include/linux/tcp_diag.h index ceee962e1d15..7a5996743946 100644 --- a/include/linux/tcp_diag.h +++ b/include/linux/tcp_diag.h @@ -99,9 +99,10 @@ enum TCPDIAG_MEMINFO, TCPDIAG_INFO, TCPDIAG_VEGASINFO, + TCPDIAG_CONG, }; -#define TCPDIAG_MAX TCPDIAG_VEGASINFO +#define TCPDIAG_MAX TCPDIAG_CONG /* TCPDIAG_MEM */ @@ -123,5 +124,4 @@ struct tcpvegas_info { __u32 tcpv_minrtt; }; - #endif /* _TCP_DIAG_H_ */ diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h new file mode 100644 index 000000000000..941f45ac117a --- /dev/null +++ b/include/linux/textsearch.h @@ -0,0 +1,180 @@ +#ifndef __LINUX_TEXTSEARCH_H +#define __LINUX_TEXTSEARCH_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include + +struct ts_config; + +/** + * TS_AUTOLOAD - Automatically load textsearch modules when needed + */ +#define TS_AUTOLOAD 1 + +/** + * struct ts_state - search state + * @offset: offset for next match + * @cb: control buffer, for persistant variables of get_next_block() + */ +struct ts_state +{ + unsigned int offset; + char cb[40]; +}; + +/** + * struct ts_ops - search module operations + * @name: name of search algorithm + * @init: initialization function to prepare a search + * @find: find the next occurrence of the pattern + * @destroy: destroy algorithm specific parts of a search configuration + * @get_pattern: return head of pattern + * @get_pattern_len: return length of pattern + * @owner: module reference to algorithm + */ +struct ts_ops +{ + const char *name; + struct ts_config * (*init)(const void *, unsigned int, int); + unsigned int (*find)(struct ts_config *, + struct ts_state *); + void (*destroy)(struct ts_config *); + void * (*get_pattern)(struct ts_config *); + unsigned int (*get_pattern_len)(struct ts_config *); + struct module *owner; + struct list_head list; +}; + +/** + * struct ts_config - search configuration + * @ops: operations of chosen algorithm + * @get_next_block: callback to fetch the next block to search in + * @finish: callback to finalize a search + */ +struct ts_config +{ + struct ts_ops *ops; + + /** + * get_next_block - fetch next block of data + * @consumed: number of bytes consumed by the caller + * @dst: destination buffer + * @conf: search configuration + * @state: search state + * + * Called repeatedly until 0 is returned. Must assign the + * head of the next block of data to &*dst and return the length + * of the block or 0 if at the end. consumed == 0 indicates + * a new search. May store/read persistant values in state->cb. + */ + unsigned int (*get_next_block)(unsigned int consumed, + const u8 **dst, + struct ts_config *conf, + struct ts_state *state); + + /** + * finish - finalize/clean a series of get_next_block() calls + * @conf: search configuration + * @state: search state + * + * Called after the last use of get_next_block(), may be used + * to cleanup any leftovers. + */ + void (*finish)(struct ts_config *conf, + struct ts_state *state); +}; + +/** + * textsearch_next - continue searching for a pattern + * @conf: search configuration + * @state: search state + * + * Continues a search looking for more occurrences of the pattern. + * textsearch_find() must be called to find the first occurrence + * in order to reset the state. + * + * Returns the position of the next occurrence of the pattern or + * UINT_MAX if not match was found. + */ +static inline unsigned int textsearch_next(struct ts_config *conf, + struct ts_state *state) +{ + unsigned int ret = conf->ops->find(conf, state); + + if (conf->finish) + conf->finish(conf, state); + + return ret; +} + +/** + * textsearch_find - start searching for a pattern + * @conf: search configuration + * @state: search state + * + * Returns the position of first occurrence of the pattern or + * UINT_MAX if no match was found. + */ +static inline unsigned int textsearch_find(struct ts_config *conf, + struct ts_state *state) +{ + state->offset = 0; + return textsearch_next(conf, state); +} + +/** + * textsearch_get_pattern - return head of the pattern + * @conf: search configuration + */ +static inline void *textsearch_get_pattern(struct ts_config *conf) +{ + return conf->ops->get_pattern(conf); +} + +/** + * textsearch_get_pattern_len - return length of the pattern + * @conf: search configuration + */ +static inline unsigned int textsearch_get_pattern_len(struct ts_config *conf) +{ + return conf->ops->get_pattern_len(conf); +} + +extern int textsearch_register(struct ts_ops *); +extern int textsearch_unregister(struct ts_ops *); +extern struct ts_config *textsearch_prepare(const char *, const void *, + unsigned int, int, int); +extern void textsearch_destroy(struct ts_config *conf); +extern unsigned int textsearch_find_continuous(struct ts_config *, + struct ts_state *, + const void *, unsigned int); + + +#define TS_PRIV_ALIGNTO 8 +#define TS_PRIV_ALIGN(len) (((len) + TS_PRIV_ALIGNTO-1) & ~(TS_PRIV_ALIGNTO-1)) + +static inline struct ts_config *alloc_ts_config(size_t payload, int gfp_mask) +{ + struct ts_config *conf; + + conf = kmalloc(TS_PRIV_ALIGN(sizeof(*conf)) + payload, gfp_mask); + if (conf == NULL) + return ERR_PTR(-ENOMEM); + + memset(conf, 0, TS_PRIV_ALIGN(sizeof(*conf)) + payload); + return conf; +} + +static inline void *ts_config_priv(struct ts_config *conf) +{ + return ((u8 *) conf + TS_PRIV_ALIGN(sizeof(struct ts_config))); +} + +#endif /* __KERNEL__ */ + +#endif diff --git a/include/linux/textsearch_fsm.h b/include/linux/textsearch_fsm.h new file mode 100644 index 000000000000..fdfa078c66e5 --- /dev/null +++ b/include/linux/textsearch_fsm.h @@ -0,0 +1,48 @@ +#ifndef __LINUX_TEXTSEARCH_FSM_H +#define __LINUX_TEXTSEARCH_FSM_H + +#include + +enum { + TS_FSM_SPECIFIC, /* specific character */ + TS_FSM_WILDCARD, /* any character */ + TS_FSM_DIGIT, /* isdigit() */ + TS_FSM_XDIGIT, /* isxdigit() */ + TS_FSM_PRINT, /* isprint() */ + TS_FSM_ALPHA, /* isalpha() */ + TS_FSM_ALNUM, /* isalnum() */ + TS_FSM_ASCII, /* isascii() */ + TS_FSM_CNTRL, /* iscntrl() */ + TS_FSM_GRAPH, /* isgraph() */ + TS_FSM_LOWER, /* islower() */ + TS_FSM_UPPER, /* isupper() */ + TS_FSM_PUNCT, /* ispunct() */ + TS_FSM_SPACE, /* isspace() */ + __TS_FSM_TYPE_MAX, +}; +#define TS_FSM_TYPE_MAX (__TS_FSM_TYPE_MAX - 1) + +enum { + TS_FSM_SINGLE, /* 1 occurrence */ + TS_FSM_PERHAPS, /* 1 or 0 occurrence */ + TS_FSM_ANY, /* 0..n occurrences */ + TS_FSM_MULTI, /* 1..n occurrences */ + TS_FSM_HEAD_IGNORE, /* 0..n ignored occurrences at head */ + __TS_FSM_RECUR_MAX, +}; +#define TS_FSM_RECUR_MAX (__TS_FSM_RECUR_MAX - 1) + +/** + * struct ts_fsm_token - state machine token (state) + * @type: type of token + * @recur: number of recurrences + * @value: character value for TS_FSM_SPECIFIC + */ +struct ts_fsm_token +{ + __u16 type; + __u8 recur; + __u8 value; +}; + +#endif diff --git a/include/linux/timer.h b/include/linux/timer.h index 90db1cc62ddd..221f81ac2002 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -6,45 +6,33 @@ #include #include -struct tvec_t_base_s; +struct timer_base_s; struct timer_list { struct list_head entry; unsigned long expires; - spinlock_t lock; unsigned long magic; void (*function)(unsigned long); unsigned long data; - struct tvec_t_base_s *base; + struct timer_base_s *base; }; #define TIMER_MAGIC 0x4b87ad6e +extern struct timer_base_s __init_timer_base; + #define TIMER_INITIALIZER(_function, _expires, _data) { \ .function = (_function), \ .expires = (_expires), \ .data = (_data), \ - .base = NULL, \ + .base = &__init_timer_base, \ .magic = TIMER_MAGIC, \ - .lock = SPIN_LOCK_UNLOCKED, \ } -/*** - * init_timer - initialize a timer. - * @timer: the timer to be initialized - * - * init_timer() must be done to a timer prior calling *any* of the - * other timer functions. - */ -static inline void init_timer(struct timer_list * timer) -{ - timer->base = NULL; - timer->magic = TIMER_MAGIC; - spin_lock_init(&timer->lock); -} +void fastcall init_timer(struct timer_list * timer); /*** * timer_pending - is a timer pending? @@ -58,7 +46,7 @@ static inline void init_timer(struct timer_list * timer) */ static inline int timer_pending(const struct timer_list * timer) { - return timer->base != NULL; + return timer->entry.next != NULL; } extern void add_timer_on(struct timer_list *timer, int cpu); @@ -88,13 +76,15 @@ static inline void add_timer(struct timer_list * timer) } #ifdef CONFIG_SMP + extern int try_to_del_timer_sync(struct timer_list *timer); extern int del_timer_sync(struct timer_list *timer); - extern int del_singleshot_timer_sync(struct timer_list *timer); #else -# define del_timer_sync(t) del_timer(t) -# define del_singleshot_timer_sync(t) del_timer(t) +# define try_to_del_timer_sync(t) del_timer(t) +# define del_timer_sync(t) del_timer(t) #endif +#define del_singleshot_timer_sync(t) del_timer_sync(t) + extern void init_timers(void); extern void run_local_timers(void); extern void it_real_fn(unsigned long); diff --git a/include/linux/tty.h b/include/linux/tty.h index 1b76106272d3..59ff42c629ec 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -345,6 +345,7 @@ extern int tty_check_change(struct tty_struct * tty); extern void stop_tty(struct tty_struct * tty); extern void start_tty(struct tty_struct * tty); extern int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc); +extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); extern void tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev); diff --git a/include/linux/usb.h b/include/linux/usb.h index 3d508bf08402..eb282b581546 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -290,7 +290,7 @@ struct usb_bus { struct class_device *class_dev; /* class device for this bus */ struct kref kref; /* handles reference counting this bus */ void (*release)(struct usb_bus *bus); /* function to destroy this bus's memory */ -#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) +#if defined(CONFIG_USB_MON) struct mon_bus *mon_bus; /* non-null when associated */ int monitored; /* non-zero when monitored */ #endif diff --git a/include/linux/wait.h b/include/linux/wait.h index c9486c3efb4a..d38c9fecdc36 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -33,7 +33,7 @@ int default_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key struct __wait_queue { unsigned int flags; #define WQ_FLAG_EXCLUSIVE 0x01 - struct task_struct * task; + void *private; wait_queue_func_t func; struct list_head task_list; }; @@ -60,7 +60,7 @@ typedef struct __wait_queue_head wait_queue_head_t; */ #define __WAITQUEUE_INITIALIZER(name, tsk) { \ - .task = tsk, \ + .private = tsk, \ .func = default_wake_function, \ .task_list = { NULL, NULL } } @@ -86,7 +86,7 @@ static inline void init_waitqueue_head(wait_queue_head_t *q) static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p) { q->flags = 0; - q->task = p; + q->private = p; q->func = default_wake_function; } @@ -94,7 +94,7 @@ static inline void init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func) { q->flags = 0; - q->task = NULL; + q->private = NULL; q->func = func; } @@ -110,7 +110,7 @@ static inline int waitqueue_active(wait_queue_head_t *q) * aio specifies a wait queue entry with an async notification * callback routine, not associated with any task. */ -#define is_sync_wait(wait) (!(wait) || ((wait)->task)) +#define is_sync_wait(wait) (!(wait) || ((wait)->private)) extern void FASTCALL(add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)); extern void FASTCALL(add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait)); @@ -384,7 +384,7 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); #define DEFINE_WAIT(name) \ wait_queue_t name = { \ - .task = current, \ + .private = current, \ .func = autoremove_wake_function, \ .task_list = LIST_HEAD_INIT((name).task_list), \ } @@ -393,7 +393,7 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); struct wait_bit_queue name = { \ .key = __WAIT_BIT_KEY_INITIALIZER(word, bit), \ .wait = { \ - .task = current, \ + .private = current, \ .func = wake_bit_function, \ .task_list = \ LIST_HEAD_INIT((name).wait.task_list), \ @@ -402,7 +402,7 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); #define init_wait(wait) \ do { \ - (wait)->task = current; \ + (wait)->private = current; \ (wait)->func = autoremove_wake_function; \ INIT_LIST_HEAD(&(wait)->task_list); \ } while (0) diff --git a/include/media/audiochip.h b/include/media/audiochip.h index d3e9e30608dc..f345a61c3bdb 100644 --- a/include/media/audiochip.h +++ b/include/media/audiochip.h @@ -1,3 +1,7 @@ +/* + * $Id: audiochip.h,v 1.3 2005/06/12 04:19:19 mchehab Exp $ + */ + #ifndef AUDIOCHIP_H #define AUDIOCHIP_H diff --git a/include/media/id.h b/include/media/id.h index 1b0320dc8f73..a39a6423914b 100644 --- a/include/media/id.h +++ b/include/media/id.h @@ -1,3 +1,7 @@ +/* + * $Id: id.h,v 1.4 2005/06/12 04:19:19 mchehab Exp $ + */ + /* FIXME: this temporarely, until these are included in linux/i2c-id.h */ /* drivers */ diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 62c963a52d86..698670547f16 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -1,5 +1,5 @@ /* - * $Id: ir-common.h,v 1.8 2005/02/22 12:28:40 kraxel Exp $ + * $Id: ir-common.h,v 1.9 2005/05/15 19:01:26 mchehab Exp $ * * some common structs and functions to handle infrared remotes via * input layer ... @@ -50,6 +50,7 @@ extern IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE]; void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, IR_KEYTAB_TYPE *ir_codes); diff --git a/include/media/tuner.h b/include/media/tuner.h index 156a9c51ffec..2dd8310901e8 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -25,6 +25,8 @@ #include "id.h" +#define ADDR_UNSET (255) + #define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ #define TUNER_PHILIPS_PAL_I 1 #define TUNER_PHILIPS_NTSC 2 @@ -98,12 +100,23 @@ #define TUNER_PHILIPS_FQ1216AME_MK4 56 /* Hauppauge PVR-150 PAL */ #define TUNER_PHILIPS_FQ1236A_MK4 57 /* Hauppauge PVR-500MCE NTSC */ +#define TUNER_YMEC_TVF_8531MF 58 +#define TUNER_YMEC_TVF_5533MF 59 /* Pixelview Pro Ultra NTSC */ +#define TUNER_THOMSON_DTT7611 60 +#define TUNER_TENA_9533_DI 61 +#define TUNER_TEA5767 62 /* Only FM Radio Tuner */ + +#define TEA5767_TUNER_NAME "Philips TEA5767HN FM Radio" + +#define TUNER_THOMSON_DTT7611 60 + #define NOTUNER 0 #define PAL 1 /* PAL_BG */ #define PAL_I 2 #define NTSC 3 #define SECAM 4 #define ATSC 5 +#define RADIO 6 #define NoTuner 0 #define Philips 1 @@ -119,10 +132,20 @@ #define TCL 11 #define THOMSON 12 +enum v4l_radio_tuner { + TEA5767_LOW_LO_32768 = 0, + TEA5767_HIGH_LO_32768 = 1, + TEA5767_LOW_LO_13MHz = 2, + TEA5767_HIGH_LO_13MHz = 3, +}; + + #define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ #define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ +#define TUNER_SET_TYPE_ADDR _IOW('T',3,int) /* set tuner type and I2C addr */ #define TDA9887_SET_CONFIG _IOW('t',5,int) + /* tv card specific */ # define TDA9887_PRESENT (1<<0) # define TDA9887_PORT1_INACTIVE (1<<1) @@ -143,6 +166,12 @@ #define I2C_ADDR_TDA8290 0x4b #define I2C_ADDR_TDA8275 0x61 +struct tuner_addr { + enum v4l2_tuner_type v4l2_tuner; + unsigned int type; + unsigned short addr; +}; + struct tuner { /* device */ struct i2c_client i2c; diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h index 627603e561a6..5c4fe30e8d1d 100644 --- a/include/media/tveeprom.h +++ b/include/media/tveeprom.h @@ -1,3 +1,7 @@ +/* + * $Id: tveeprom.h,v 1.2 2005/06/12 04:19:19 mchehab Exp $ + */ + struct tveeprom { u32 has_radio; diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index a53e08a45e32..88d9fe5975d5 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -131,7 +131,6 @@ sctp_state_fn_t sctp_sf_do_ecne; sctp_state_fn_t sctp_sf_ootb; sctp_state_fn_t sctp_sf_pdiscard; sctp_state_fn_t sctp_sf_violation; -sctp_state_fn_t sctp_sf_violation_chunklen; sctp_state_fn_t sctp_sf_discard_chunk; sctp_state_fn_t sctp_sf_do_5_2_1_siminit; sctp_state_fn_t sctp_sf_do_5_2_2_dupinit; @@ -259,11 +258,6 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *); -sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, - __u16 error, - const struct sctp_association *asoc, - struct sctp_transport *transport); - /* Prototypes for statetable processing. */ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, diff --git a/include/net/tcp.h b/include/net/tcp.h index f730935b824a..ec9e20c27179 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -505,25 +505,6 @@ static __inline__ int tcp_sk_listen_hashfn(struct sock *sk) #else # define TCP_TW_RECYCLE_TICK (12+2-TCP_TW_RECYCLE_SLOTS_LOG) #endif - -#define BICTCP_BETA_SCALE 1024 /* Scale factor beta calculation - * max_cwnd = snd_cwnd * beta - */ -#define BICTCP_MAX_INCREMENT 32 /* - * Limit on the amount of - * increment allowed during - * binary search. - */ -#define BICTCP_FUNC_OF_MIN_INCR 11 /* - * log(B/Smin)/log(B/(B-1))+1, - * Smin:min increment - * B:log factor - */ -#define BICTCP_B 4 /* - * In binary search, - * go to point (max+min)/N - */ - /* * TCP option */ @@ -596,16 +577,7 @@ extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_tw_reuse; extern int sysctl_tcp_frto; extern int sysctl_tcp_low_latency; -extern int sysctl_tcp_westwood; -extern int sysctl_tcp_vegas_cong_avoid; -extern int sysctl_tcp_vegas_alpha; -extern int sysctl_tcp_vegas_beta; -extern int sysctl_tcp_vegas_gamma; extern int sysctl_tcp_nometrics_save; -extern int sysctl_tcp_bic; -extern int sysctl_tcp_bic_fast_convergence; -extern int sysctl_tcp_bic_low_window; -extern int sysctl_tcp_bic_beta; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; @@ -1136,6 +1108,82 @@ static inline void tcp_packets_out_dec(struct tcp_sock *tp, tp->packets_out -= tcp_skb_pcount(skb); } +/* Events passed to congestion control interface */ +enum tcp_ca_event { + CA_EVENT_TX_START, /* first transmit when no packets in flight */ + CA_EVENT_CWND_RESTART, /* congestion window restart */ + CA_EVENT_COMPLETE_CWR, /* end of congestion recovery */ + CA_EVENT_FRTO, /* fast recovery timeout */ + CA_EVENT_LOSS, /* loss timeout */ + CA_EVENT_FAST_ACK, /* in sequence ack */ + CA_EVENT_SLOW_ACK, /* other ack */ +}; + +/* + * Interface for adding new TCP congestion control handlers + */ +#define TCP_CA_NAME_MAX 16 +struct tcp_congestion_ops { + struct list_head list; + + /* initialize private data (optional) */ + void (*init)(struct tcp_sock *tp); + /* cleanup private data (optional) */ + void (*release)(struct tcp_sock *tp); + + /* return slow start threshold (required) */ + u32 (*ssthresh)(struct tcp_sock *tp); + /* lower bound for congestion window (optional) */ + u32 (*min_cwnd)(struct tcp_sock *tp); + /* do new cwnd calculation (required) */ + void (*cong_avoid)(struct tcp_sock *tp, u32 ack, + u32 rtt, u32 in_flight, int good_ack); + /* round trip time sample per acked packet (optional) */ + void (*rtt_sample)(struct tcp_sock *tp, u32 usrtt); + /* call before changing ca_state (optional) */ + void (*set_state)(struct tcp_sock *tp, u8 new_state); + /* call when cwnd event occurs (optional) */ + void (*cwnd_event)(struct tcp_sock *tp, enum tcp_ca_event ev); + /* new value of cwnd after loss (optional) */ + u32 (*undo_cwnd)(struct tcp_sock *tp); + /* hook for packet ack accounting (optional) */ + void (*pkts_acked)(struct tcp_sock *tp, u32 num_acked); + /* get info for tcp_diag (optional) */ + void (*get_info)(struct tcp_sock *tp, u32 ext, struct sk_buff *skb); + + char name[TCP_CA_NAME_MAX]; + struct module *owner; +}; + +extern int tcp_register_congestion_control(struct tcp_congestion_ops *type); +extern void tcp_unregister_congestion_control(struct tcp_congestion_ops *type); + +extern void tcp_init_congestion_control(struct tcp_sock *tp); +extern void tcp_cleanup_congestion_control(struct tcp_sock *tp); +extern int tcp_set_default_congestion_control(const char *name); +extern void tcp_get_default_congestion_control(char *name); +extern int tcp_set_congestion_control(struct tcp_sock *tp, const char *name); + +extern struct tcp_congestion_ops tcp_init_congestion_ops; +extern u32 tcp_reno_ssthresh(struct tcp_sock *tp); +extern void tcp_reno_cong_avoid(struct tcp_sock *tp, u32 ack, + u32 rtt, u32 in_flight, int flag); +extern u32 tcp_reno_min_cwnd(struct tcp_sock *tp); +extern struct tcp_congestion_ops tcp_reno; + +static inline void tcp_set_ca_state(struct tcp_sock *tp, u8 ca_state) +{ + if (tp->ca_ops->set_state) + tp->ca_ops->set_state(tp, ca_state); + tp->ca_state = ca_state; +} + +static inline void tcp_ca_event(struct tcp_sock *tp, enum tcp_ca_event event) +{ + if (tp->ca_ops->cwnd_event) + tp->ca_ops->cwnd_event(tp, event); +} + /* This determines how many packets are "in the network" to the best * of our knowledge. In many cases it is conservative, but where * detailed information is available from the receiver (via SACK @@ -1155,91 +1203,6 @@ static __inline__ unsigned int tcp_packets_in_flight(const struct tcp_sock *tp) return (tp->packets_out - tp->left_out + tp->retrans_out); } -/* - * Which congestion algorithim is in use on the connection. - */ -#define tcp_is_vegas(__tp) ((__tp)->adv_cong == TCP_VEGAS) -#define tcp_is_westwood(__tp) ((__tp)->adv_cong == TCP_WESTWOOD) -#define tcp_is_bic(__tp) ((__tp)->adv_cong == TCP_BIC) - -/* Recalculate snd_ssthresh, we want to set it to: - * - * Reno: - * one half the current congestion window, but no - * less than two segments - * - * BIC: - * behave like Reno until low_window is reached, - * then increase congestion window slowly - */ -static inline __u32 tcp_recalc_ssthresh(struct tcp_sock *tp) -{ - if (tcp_is_bic(tp)) { - if (sysctl_tcp_bic_fast_convergence && - tp->snd_cwnd < tp->bictcp.last_max_cwnd) - tp->bictcp.last_max_cwnd = (tp->snd_cwnd * - (BICTCP_BETA_SCALE - + sysctl_tcp_bic_beta)) - / (2 * BICTCP_BETA_SCALE); - else - tp->bictcp.last_max_cwnd = tp->snd_cwnd; - - if (tp->snd_cwnd > sysctl_tcp_bic_low_window) - return max((tp->snd_cwnd * sysctl_tcp_bic_beta) - / BICTCP_BETA_SCALE, 2U); - } - - return max(tp->snd_cwnd >> 1U, 2U); -} - -/* Stop taking Vegas samples for now. */ -#define tcp_vegas_disable(__tp) ((__tp)->vegas.doing_vegas_now = 0) - -static inline void tcp_vegas_enable(struct tcp_sock *tp) -{ - /* There are several situations when we must "re-start" Vegas: - * - * o when a connection is established - * o after an RTO - * o after fast recovery - * o when we send a packet and there is no outstanding - * unacknowledged data (restarting an idle connection) - * - * In these circumstances we cannot do a Vegas calculation at the - * end of the first RTT, because any calculation we do is using - * stale info -- both the saved cwnd and congestion feedback are - * stale. - * - * Instead we must wait until the completion of an RTT during - * which we actually receive ACKs. - */ - - /* Begin taking Vegas samples next time we send something. */ - tp->vegas.doing_vegas_now = 1; - - /* Set the beginning of the next send window. */ - tp->vegas.beg_snd_nxt = tp->snd_nxt; - - tp->vegas.cntRTT = 0; - tp->vegas.minRTT = 0x7fffffff; -} - -/* Should we be taking Vegas samples right now? */ -#define tcp_vegas_enabled(__tp) ((__tp)->vegas.doing_vegas_now) - -extern void tcp_ca_init(struct tcp_sock *tp); - -static inline void tcp_set_ca_state(struct tcp_sock *tp, u8 ca_state) -{ - if (tcp_is_vegas(tp)) { - if (ca_state == TCP_CA_Open) - tcp_vegas_enable(tp); - else - tcp_vegas_disable(tp); - } - tp->ca_state = ca_state; -} - /* If cwnd > ssthresh, we may raise ssthresh to be half-way to cwnd. * The exception is rate halving phase, when cwnd is decreasing towards * ssthresh. @@ -1288,7 +1251,7 @@ static inline void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp) static inline void __tcp_enter_cwr(struct tcp_sock *tp) { tp->undo_marker = 0; - tp->snd_ssthresh = tcp_recalc_ssthresh(tp); + tp->snd_ssthresh = tp->ca_ops->ssthresh(tp); tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp) + 1U); tp->snd_cwnd_cnt = 0; @@ -1876,52 +1839,4 @@ struct tcp_iter_state { extern int tcp_proc_register(struct tcp_seq_afinfo *afinfo); extern void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo); -/* TCP Westwood functions and constants */ - -#define TCP_WESTWOOD_INIT_RTT (20*HZ) /* maybe too conservative?! */ -#define TCP_WESTWOOD_RTT_MIN (HZ/20) /* 50ms */ - -static inline void tcp_westwood_update_rtt(struct tcp_sock *tp, __u32 rtt_seq) -{ - if (tcp_is_westwood(tp)) - tp->westwood.rtt = rtt_seq; -} - -static inline __u32 __tcp_westwood_bw_rttmin(const struct tcp_sock *tp) -{ - return max((tp->westwood.bw_est) * (tp->westwood.rtt_min) / - (__u32) (tp->mss_cache_std), - 2U); -} - -static inline __u32 tcp_westwood_bw_rttmin(const struct tcp_sock *tp) -{ - return tcp_is_westwood(tp) ? __tcp_westwood_bw_rttmin(tp) : 0; -} - -static inline int tcp_westwood_ssthresh(struct tcp_sock *tp) -{ - __u32 ssthresh = 0; - - if (tcp_is_westwood(tp)) { - ssthresh = __tcp_westwood_bw_rttmin(tp); - if (ssthresh) - tp->snd_ssthresh = ssthresh; - } - - return (ssthresh != 0); -} - -static inline int tcp_westwood_cwnd(struct tcp_sock *tp) -{ - __u32 cwnd = 0; - - if (tcp_is_westwood(tp)) { - cwnd = __tcp_westwood_bw_rttmin(tp); - if (cwnd) - tp->snd_cwnd = cwnd; - } - - return (cwnd != 0); -} #endif /* _TCP_H */ diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index 6d3413a56708..67b867f31fe4 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -77,6 +77,11 @@ extern socket_state_t dead_socket; /* Use this just for bridge windows */ #define MAP_IOSPACE 0x20 +/* power hook operations */ +#define HOOK_POWER_PRE 0x01 +#define HOOK_POWER_POST 0x02 + + typedef struct pccard_io_map { u_char map; u_char flags; @@ -222,6 +227,9 @@ struct pcmcia_socket { /* Zoom video behaviour is so chip specific its not worth adding this to _ops */ void (*zoom_video)(struct pcmcia_socket *, int); + + /* so is power hook */ + int (*power_hook)(struct pcmcia_socket *sock, int operation); /* state thread */ struct semaphore skt_sem; /* protects socket h/w state */ diff --git a/include/scsi/sg_request.h b/include/scsi/sg_request.h new file mode 100644 index 000000000000..57ff525bdd3b --- /dev/null +++ b/include/scsi/sg_request.h @@ -0,0 +1,26 @@ +typedef struct scsi_request Scsi_Request; + +static Scsi_Request *dummy_cmdp; /* only used for sizeof */ + +typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ + unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ + unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */ + unsigned bufflen; /* Size of (aggregate) data buffer */ + unsigned b_malloc_len; /* actual len malloc'ed in buffer */ + void *buffer; /* Data buffer or scatter list (k_use_sg>0) */ + char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ + unsigned char cmd_opcode; /* first byte of command */ +} Sg_scatter_hold; + +typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ + Scsi_Request *my_cmdp; /* != 0 when request with lower levels */ + struct sg_request *nextrp; /* NULL -> tail request (slist) */ + struct sg_fd *parentfp; /* NULL -> not in use */ + Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ + sg_io_hdr_t header; /* scsi command+info, see */ + unsigned char sense_b[sizeof (dummy_cmdp->sr_sense_buffer)]; + char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ + char orphan; /* 1 -> drop on sight, 0 -> normal */ + char sg_io_owned; /* 1 -> packet belongs to SG_IO */ + volatile char done; /* 0->before bh, 1->before read, 2->read */ +} Sg_request; diff --git a/include/sound/core.h b/include/sound/core.h index 9117c23e3a01..f8c4ef0aa352 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -292,6 +292,7 @@ void *snd_hidden_kcalloc(size_t n, size_t size, int flags); void snd_hidden_kfree(const void *obj); void *snd_hidden_vmalloc(unsigned long size); void snd_hidden_vfree(void *obj); +char *snd_hidden_kstrdup(const char *s, int flags); #define kmalloc(size, flags) snd_hidden_kmalloc(size, flags) #define kcalloc(n, size, flags) snd_hidden_kcalloc(n, size, flags) #define kfree(obj) snd_hidden_kfree(obj) @@ -301,6 +302,7 @@ void snd_hidden_vfree(void *obj); #define vmalloc_nocheck(size) snd_wrapper_vmalloc(size) #define kfree_nocheck(obj) snd_wrapper_kfree(obj) #define vfree_nocheck(obj) snd_wrapper_vfree(obj) +#define kstrdup(s, flags) snd_hidden_kstrdup(s, flags) #else #define snd_memory_init() /*NOP*/ #define snd_memory_done() /*NOP*/ @@ -311,7 +313,6 @@ void snd_hidden_vfree(void *obj); #define kfree_nocheck(obj) kfree(obj) #define vfree_nocheck(obj) vfree(obj) #endif -char *snd_kmalloc_strdup(const char *string, int flags); int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count); int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count); diff --git a/init/calibrate.c b/init/calibrate.c index c698e04a3dbe..d206c7548fe6 100644 --- a/init/calibrate.c +++ b/init/calibrate.c @@ -8,6 +8,8 @@ #include #include +#include + static unsigned long preset_lpj; static int __init lpj_setup(char *str) { @@ -17,6 +19,92 @@ static int __init lpj_setup(char *str) __setup("lpj=", lpj_setup); +#ifdef ARCH_HAS_READ_CURRENT_TIMER + +/* This routine uses the read_current_timer() routine and gets the + * loops per jiffy directly, instead of guessing it using delay(). + * Also, this code tries to handle non-maskable asynchronous events + * (like SMIs) + */ +#define DELAY_CALIBRATION_TICKS ((HZ < 100) ? 1 : (HZ/100)) +#define MAX_DIRECT_CALIBRATION_RETRIES 5 + +static unsigned long __devinit calibrate_delay_direct(void) +{ + unsigned long pre_start, start, post_start; + unsigned long pre_end, end, post_end; + unsigned long start_jiffies; + unsigned long tsc_rate_min, tsc_rate_max; + unsigned long good_tsc_sum = 0; + unsigned long good_tsc_count = 0; + int i; + + if (read_current_timer(&pre_start) < 0 ) + return 0; + + /* + * A simple loop like + * while ( jiffies < start_jiffies+1) + * start = read_current_timer(); + * will not do. As we don't really know whether jiffy switch + * happened first or timer_value was read first. And some asynchronous + * event can happen between these two events introducing errors in lpj. + * + * So, we do + * 1. pre_start <- When we are sure that jiffy switch hasn't happened + * 2. check jiffy switch + * 3. start <- timer value before or after jiffy switch + * 4. post_start <- When we are sure that jiffy switch has happened + * + * Note, we don't know anything about order of 2 and 3. + * Now, by looking at post_start and pre_start difference, we can + * check whether any asynchronous event happened or not + */ + + for (i = 0; i < MAX_DIRECT_CALIBRATION_RETRIES; i++) { + pre_start = 0; + read_current_timer(&start); + start_jiffies = jiffies; + while (jiffies <= (start_jiffies + 1)) { + pre_start = start; + read_current_timer(&start); + } + read_current_timer(&post_start); + + pre_end = 0; + end = post_start; + while (jiffies <= + (start_jiffies + 1 + DELAY_CALIBRATION_TICKS)) { + pre_end = end; + read_current_timer(&end); + } + read_current_timer(&post_end); + + tsc_rate_max = (post_end - pre_start) / DELAY_CALIBRATION_TICKS; + tsc_rate_min = (pre_end - post_start) / DELAY_CALIBRATION_TICKS; + + /* + * If the upper limit and lower limit of the tsc_rate is + * >= 12.5% apart, redo calibration. + */ + if (pre_start != 0 && pre_end != 0 && + (tsc_rate_max - tsc_rate_min) < (tsc_rate_max >> 3)) { + good_tsc_count++; + good_tsc_sum += tsc_rate_max; + } + } + + if (good_tsc_count) + return (good_tsc_sum/good_tsc_count); + + printk(KERN_WARNING "calibrate_delay_direct() failed to get a good " + "estimate for loops_per_jiffy.\nProbably due to long platform interrupts. Consider using \"lpj=\" boot option.\n"); + return 0; +} +#else +static unsigned long __devinit calibrate_delay_direct(void) {return 0;} +#endif + /* * This is the number of bits of precision for the loops_per_jiffy. Each * bit takes on average 1.5/HZ seconds. This (like the original) is a little @@ -35,6 +123,12 @@ void __devinit calibrate_delay(void) "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ), (loops_per_jiffy/(5000/HZ)) % 100); + } else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) { + printk("Calibrating delay using timer specific routine.. "); + printk("%lu.%02lu BogoMIPS (lpj=%lu)\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100, + loops_per_jiffy); } else { loops_per_jiffy = (1<<12); diff --git a/ipc/sem.c b/ipc/sem.c index 5ad7ac0ed60d..7e8a25c82ef3 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1054,7 +1054,7 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, struct sembuf fast_sops[SEMOPM_FAST]; struct sembuf* sops = fast_sops, *sop; struct sem_undo *un; - int undos = 0, decrease = 0, alter = 0, max; + int undos = 0, alter = 0, max; struct sem_queue queue; unsigned long jiffies_left = 0; @@ -1089,13 +1089,10 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, if (sop->sem_num >= max) max = sop->sem_num; if (sop->sem_flg & SEM_UNDO) - undos++; - if (sop->sem_op < 0) - decrease = 1; - if (sop->sem_op > 0) + undos = 1; + if (sop->sem_op != 0) alter = 1; } - alter |= decrease; retry_undos: if (undos) { diff --git a/kernel/Kconfig.hz b/kernel/Kconfig.hz new file mode 100644 index 000000000000..248e1c396f8b --- /dev/null +++ b/kernel/Kconfig.hz @@ -0,0 +1,46 @@ +# +# Timer Interrupt Frequency Configuration +# + +choice + prompt "Timer frequency" + default HZ_250 + help + Allows the configuration of the timer frequency. It is customary + to have the timer interrupt run at 1000 HZ but 100 HZ may be more + beneficial for servers and NUMA systems that do not need to have + a fast response for user interaction and that may experience bus + contention and cacheline bounces as a result of timer interrupts. + Note that the timer interrupt occurs on each processor in an SMP + environment leading to NR_CPUS * HZ number of timer interrupts + per second. + + + config HZ_100 + bool "100 HZ" + help + 100 HZ is a typical choice for servers, SMP and NUMA systems + with lots of processors that may show reduced performance if + too many timer interrupts are occurring. + + config HZ_250 + bool "250 HZ" + help + 250 HZ is a good compromise choice allowing server performance + while also showing good interactive responsiveness even + on SMP and NUMA systems. + + config HZ_1000 + bool "1000 HZ" + help + 1000 HZ is the preferred choice for desktop systems and other + systems requiring fast interactive responses to events. + +endchoice + +config HZ + int + default 100 if HZ_100 + default 250 if HZ_250 + default 1000 if HZ_1000 + diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 00e8f2575512..79dd929f4084 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -228,13 +228,7 @@ static struct dentry_operations cpuset_dops = { static struct dentry *cpuset_get_dentry(struct dentry *parent, const char *name) { - struct qstr qstr; - struct dentry *d; - - qstr.name = name; - qstr.len = strlen(name); - qstr.hash = full_name_hash(name, qstr.len); - d = lookup_hash(&qstr, parent); + struct dentry *d = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(d)) d->d_op = &cpuset_dops; return d; diff --git a/kernel/exit.c b/kernel/exit.c index 2ef2ad540201..3ebcd60a19c6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -72,6 +72,11 @@ repeat: BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); __exit_signal(p); __exit_sighand(p); + /* + * Note that the fastpath in sys_times depends on __exit_signal having + * updated the counters before a task is removed from the tasklist of + * the process by __unhash_process. + */ __unhash_process(p); /* @@ -793,6 +798,17 @@ fastcall NORET_TYPE void do_exit(long code) ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); } + /* + * We're taking recursive faults here in do_exit. Safest is to just + * leave this task alone and wait for reboot. + */ + if (unlikely(tsk->flags & PF_EXITING)) { + printk(KERN_ALERT + "Fixing recursive fault but reboot is needed!\n"); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + } + tsk->flags |= PF_EXITING; /* diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index f6297c306905..ba039e827d58 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -45,7 +45,7 @@ __report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) } } -void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) +static void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) { static int count = 100; diff --git a/kernel/kmod.c b/kernel/kmod.c index eed53d4f5230..44166e3bb8af 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -120,6 +120,7 @@ struct subprocess_info { char *path; char **argv; char **envp; + struct key *ring; int wait; int retval; }; @@ -130,16 +131,21 @@ struct subprocess_info { static int ____call_usermodehelper(void *data) { struct subprocess_info *sub_info = data; + struct key *old_session; int retval; - /* Unblock all signals. */ + /* Unblock all signals and set the session keyring. */ + key_get(sub_info->ring); flush_signals(current); spin_lock_irq(¤t->sighand->siglock); + old_session = __install_session_keyring(current, sub_info->ring); flush_signal_handlers(current, 1); sigemptyset(¤t->blocked); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + key_put(old_session); + /* We can run anywhere, unlike our parent keventd(). */ set_cpus_allowed(current, CPU_MASK_ALL); @@ -211,10 +217,11 @@ static void __call_usermodehelper(void *data) } /** - * call_usermodehelper - start a usermode application + * call_usermodehelper_keys - start a usermode application * @path: pathname for the application * @argv: null-terminated argument list * @envp: null-terminated environment list + * @session_keyring: session keyring for process (NULL for an empty keyring) * @wait: wait for the application to finish and return status. * * Runs a user-space application. The application is started @@ -224,7 +231,8 @@ static void __call_usermodehelper(void *data) * Must be called from process context. Returns a negative error code * if program was not execed successfully, or 0. */ -int call_usermodehelper(char *path, char **argv, char **envp, int wait) +int call_usermodehelper_keys(char *path, char **argv, char **envp, + struct key *session_keyring, int wait) { DECLARE_COMPLETION(done); struct subprocess_info sub_info = { @@ -232,6 +240,7 @@ int call_usermodehelper(char *path, char **argv, char **envp, int wait) .path = path, .argv = argv, .envp = envp, + .ring = session_keyring, .wait = wait, .retval = 0, }; @@ -247,7 +256,7 @@ int call_usermodehelper(char *path, char **argv, char **envp, int wait) wait_for_completion(&done); return sub_info.retval; } -EXPORT_SYMBOL(call_usermodehelper); +EXPORT_SYMBOL(call_usermodehelper_keys); void __init usermodehelper_init(void) { diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 037142b72a49..334f37472c56 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -27,6 +27,9 @@ * interface to access function arguments. * 2004-Sep Prasanna S Panchamukhi Changed Kprobes * exceptions notifier to be first on the priority list. + * 2005-May Hien Nguyen , Jim Keniston + * and Prasanna S Panchamukhi + * added function-return probes. */ #include #include @@ -41,6 +44,7 @@ #define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS) static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; +static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; unsigned int kprobe_cpu = NR_CPUS; static DEFINE_SPINLOCK(kprobe_lock); @@ -78,22 +82,23 @@ struct kprobe *get_kprobe(void *addr) * Aggregate handlers for multiple kprobes support - these handlers * take care of invoking the individual kprobe handlers on p->list */ -int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs) +static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs) { struct kprobe *kp; list_for_each_entry(kp, &p->list, list) { if (kp->pre_handler) { curr_kprobe = kp; - kp->pre_handler(kp, regs); - curr_kprobe = NULL; + if (kp->pre_handler(kp, regs)) + return 1; } + curr_kprobe = NULL; } return 0; } -void aggr_post_handler(struct kprobe *p, struct pt_regs *regs, - unsigned long flags) +static void aggr_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) { struct kprobe *kp; @@ -107,7 +112,8 @@ void aggr_post_handler(struct kprobe *p, struct pt_regs *regs, return; } -int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs, int trapnr) +static int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs, + int trapnr) { /* * if we faulted "during" the execution of a user specified @@ -120,19 +126,191 @@ int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs, int trapnr) return 0; } +static int aggr_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe *kp = curr_kprobe; + if (curr_kprobe && kp->break_handler) { + if (kp->break_handler(kp, regs)) { + curr_kprobe = NULL; + return 1; + } + } + curr_kprobe = NULL; + return 0; +} + +struct kprobe trampoline_p = { + .addr = (kprobe_opcode_t *) &kretprobe_trampoline, + .pre_handler = trampoline_probe_handler, + .post_handler = trampoline_post_handler +}; + +struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp) +{ + struct hlist_node *node; + struct kretprobe_instance *ri; + hlist_for_each_entry(ri, node, &rp->free_instances, uflist) + return ri; + return NULL; +} + +static struct kretprobe_instance *get_used_rp_inst(struct kretprobe *rp) +{ + struct hlist_node *node; + struct kretprobe_instance *ri; + hlist_for_each_entry(ri, node, &rp->used_instances, uflist) + return ri; + return NULL; +} + +struct kretprobe_instance *get_rp_inst(void *sara) +{ + struct hlist_head *head; + struct hlist_node *node; + struct task_struct *tsk; + struct kretprobe_instance *ri; + + tsk = arch_get_kprobe_task(sara); + head = &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]; + hlist_for_each_entry(ri, node, head, hlist) { + if (ri->stack_addr == sara) + return ri; + } + return NULL; +} + +void add_rp_inst(struct kretprobe_instance *ri) +{ + struct task_struct *tsk; + /* + * Remove rp inst off the free list - + * Add it back when probed function returns + */ + hlist_del(&ri->uflist); + tsk = arch_get_kprobe_task(ri->stack_addr); + /* Add rp inst onto table */ + INIT_HLIST_NODE(&ri->hlist); + hlist_add_head(&ri->hlist, + &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]); + + /* Also add this rp inst to the used list. */ + INIT_HLIST_NODE(&ri->uflist); + hlist_add_head(&ri->uflist, &ri->rp->used_instances); +} + +void recycle_rp_inst(struct kretprobe_instance *ri) +{ + /* remove rp inst off the rprobe_inst_table */ + hlist_del(&ri->hlist); + if (ri->rp) { + /* remove rp inst off the used list */ + hlist_del(&ri->uflist); + /* put rp inst back onto the free list */ + INIT_HLIST_NODE(&ri->uflist); + hlist_add_head(&ri->uflist, &ri->rp->free_instances); + } else + /* Unregistering */ + kfree(ri); +} + +struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk) +{ + return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]; +} + +struct kretprobe_instance *get_rp_inst_tsk(struct task_struct *tk) +{ + struct task_struct *tsk; + struct hlist_head *head; + struct hlist_node *node; + struct kretprobe_instance *ri; + + head = &kretprobe_inst_table[hash_ptr(tk, KPROBE_HASH_BITS)]; + + hlist_for_each_entry(ri, node, head, hlist) { + tsk = arch_get_kprobe_task(ri->stack_addr); + if (tsk == tk) + return ri; + } + return NULL; +} + +/* + * This function is called from do_exit or do_execv when task tk's stack is + * about to be recycled. Recycle any function-return probe instances + * associated with this task. These represent probed functions that have + * been called but may never return. + */ +void kprobe_flush_task(struct task_struct *tk) +{ + unsigned long flags = 0; + spin_lock_irqsave(&kprobe_lock, flags); + arch_kprobe_flush_task(tk); + spin_unlock_irqrestore(&kprobe_lock, flags); +} + +/* + * This kprobe pre_handler is registered with every kretprobe. When probe + * hits it will set up the return probe. + */ +static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs) +{ + struct kretprobe *rp = container_of(p, struct kretprobe, kp); + + /*TODO: consider to only swap the RA after the last pre_handler fired */ + arch_prepare_kretprobe(rp, regs); + return 0; +} + +static inline void free_rp_inst(struct kretprobe *rp) +{ + struct kretprobe_instance *ri; + while ((ri = get_free_rp_inst(rp)) != NULL) { + hlist_del(&ri->uflist); + kfree(ri); + } +} + +/* + * Keep all fields in the kprobe consistent + */ +static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p) +{ + memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t)); + memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn)); +} + +/* +* Add the new probe to old_p->list. Fail if this is the +* second jprobe at the address - two jprobes can't coexist +*/ +static int add_new_kprobe(struct kprobe *old_p, struct kprobe *p) +{ + struct kprobe *kp; + + if (p->break_handler) { + list_for_each_entry(kp, &old_p->list, list) { + if (kp->break_handler) + return -EEXIST; + } + list_add_tail(&p->list, &old_p->list); + } else + list_add(&p->list, &old_p->list); + return 0; +} + /* * Fill in the required fields of the "manager kprobe". Replace the * earlier kprobe in the hlist with the manager kprobe */ static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) { + copy_kprobe(p, ap); ap->addr = p->addr; - ap->opcode = p->opcode; - memcpy(&ap->ainsn, &p->ainsn, sizeof(struct arch_specific_insn)); - ap->pre_handler = aggr_pre_handler; ap->post_handler = aggr_post_handler; ap->fault_handler = aggr_fault_handler; + ap->break_handler = aggr_break_handler; INIT_LIST_HEAD(&ap->list); list_add(&p->list, &ap->list); @@ -153,16 +331,16 @@ static int register_aggr_kprobe(struct kprobe *old_p, struct kprobe *p) int ret = 0; struct kprobe *ap; - if (old_p->break_handler || p->break_handler) { - ret = -EEXIST; /* kprobe and jprobe can't (yet) coexist */ - } else if (old_p->pre_handler == aggr_pre_handler) { - list_add(&p->list, &old_p->list); + if (old_p->pre_handler == aggr_pre_handler) { + copy_kprobe(old_p, p); + ret = add_new_kprobe(old_p, p); } else { ap = kcalloc(1, sizeof(struct kprobe), GFP_ATOMIC); if (!ap) return -ENOMEM; add_aggr_kprobe(ap, old_p); - list_add(&p->list, &ap->list); + copy_kprobe(ap, p); + ret = add_new_kprobe(ap, p); } return ret; } @@ -170,10 +348,8 @@ static int register_aggr_kprobe(struct kprobe *old_p, struct kprobe *p) /* kprobe removal house-keeping routines */ static inline void cleanup_kprobe(struct kprobe *p, unsigned long flags) { - *p->addr = p->opcode; + arch_disarm_kprobe(p); hlist_del(&p->hlist); - flush_icache_range((unsigned long) p->addr, - (unsigned long) p->addr + sizeof(kprobe_opcode_t)); spin_unlock_irqrestore(&kprobe_lock, flags); arch_remove_kprobe(p); } @@ -200,6 +376,7 @@ int register_kprobe(struct kprobe *p) } spin_lock_irqsave(&kprobe_lock, flags); old_p = get_kprobe(p->addr); + p->nmissed = 0; if (old_p) { ret = register_aggr_kprobe(old_p, p); goto out; @@ -210,10 +387,8 @@ int register_kprobe(struct kprobe *p) hlist_add_head(&p->hlist, &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); - p->opcode = *p->addr; - *p->addr = BREAKPOINT_INSTRUCTION; - flush_icache_range((unsigned long) p->addr, - (unsigned long) p->addr + sizeof(kprobe_opcode_t)); + arch_arm_kprobe(p); + out: spin_unlock_irqrestore(&kprobe_lock, flags); rm_kprobe: @@ -257,16 +432,82 @@ void unregister_jprobe(struct jprobe *jp) unregister_kprobe(&jp->kp); } +#ifdef ARCH_SUPPORTS_KRETPROBES + +int register_kretprobe(struct kretprobe *rp) +{ + int ret = 0; + struct kretprobe_instance *inst; + int i; + + rp->kp.pre_handler = pre_handler_kretprobe; + + /* Pre-allocate memory for max kretprobe instances */ + if (rp->maxactive <= 0) { +#ifdef CONFIG_PREEMPT + rp->maxactive = max(10, 2 * NR_CPUS); +#else + rp->maxactive = NR_CPUS; +#endif + } + INIT_HLIST_HEAD(&rp->used_instances); + INIT_HLIST_HEAD(&rp->free_instances); + for (i = 0; i < rp->maxactive; i++) { + inst = kmalloc(sizeof(struct kretprobe_instance), GFP_KERNEL); + if (inst == NULL) { + free_rp_inst(rp); + return -ENOMEM; + } + INIT_HLIST_NODE(&inst->uflist); + hlist_add_head(&inst->uflist, &rp->free_instances); + } + + rp->nmissed = 0; + /* Establish function entry probe point */ + if ((ret = register_kprobe(&rp->kp)) != 0) + free_rp_inst(rp); + return ret; +} + +#else /* ARCH_SUPPORTS_KRETPROBES */ + +int register_kretprobe(struct kretprobe *rp) +{ + return -ENOSYS; +} + +#endif /* ARCH_SUPPORTS_KRETPROBES */ + +void unregister_kretprobe(struct kretprobe *rp) +{ + unsigned long flags; + struct kretprobe_instance *ri; + + unregister_kprobe(&rp->kp); + /* No race here */ + spin_lock_irqsave(&kprobe_lock, flags); + free_rp_inst(rp); + while ((ri = get_used_rp_inst(rp)) != NULL) { + ri->rp = NULL; + hlist_del(&ri->uflist); + } + spin_unlock_irqrestore(&kprobe_lock, flags); +} + static int __init init_kprobes(void) { int i, err = 0; /* FIXME allocate the probe table, currently defined statically */ /* initialize all list heads */ - for (i = 0; i < KPROBE_TABLE_SIZE; i++) + for (i = 0; i < KPROBE_TABLE_SIZE; i++) { INIT_HLIST_HEAD(&kprobe_table[i]); + INIT_HLIST_HEAD(&kretprobe_inst_table[i]); + } err = register_die_notifier(&kprobe_exceptions_nb); + /* Register the trampoline probe for return probe */ + register_kprobe(&trampoline_p); return err; } @@ -277,3 +518,6 @@ EXPORT_SYMBOL_GPL(unregister_kprobe); EXPORT_SYMBOL_GPL(register_jprobe); EXPORT_SYMBOL_GPL(unregister_jprobe); EXPORT_SYMBOL_GPL(jprobe_return); +EXPORT_SYMBOL_GPL(register_kretprobe); +EXPORT_SYMBOL_GPL(unregister_kretprobe); + diff --git a/kernel/module.c b/kernel/module.c index a566745dde62..068e271ab3a5 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -370,6 +371,43 @@ static inline void percpu_modcopy(void *pcpudst, const void *src, #endif /* CONFIG_SMP */ #ifdef CONFIG_MODULE_UNLOAD +#define MODINFO_ATTR(field) \ +static void setup_modinfo_##field(struct module *mod, const char *s) \ +{ \ + mod->field = kstrdup(s, GFP_KERNEL); \ +} \ +static ssize_t show_modinfo_##field(struct module_attribute *mattr, \ + struct module *mod, char *buffer) \ +{ \ + return sprintf(buffer, "%s\n", mod->field); \ +} \ +static int modinfo_##field##_exists(struct module *mod) \ +{ \ + return mod->field != NULL; \ +} \ +static void free_modinfo_##field(struct module *mod) \ +{ \ + kfree(mod->field); \ + mod->field = NULL; \ +} \ +static struct module_attribute modinfo_##field = { \ + .attr = { .name = __stringify(field), .mode = 0444, \ + .owner = THIS_MODULE }, \ + .show = show_modinfo_##field, \ + .setup = setup_modinfo_##field, \ + .test = modinfo_##field##_exists, \ + .free = free_modinfo_##field, \ +}; + +MODINFO_ATTR(version); +MODINFO_ATTR(srcversion); + +static struct module_attribute *modinfo_attrs[] = { + &modinfo_version, + &modinfo_srcversion, + NULL, +}; + /* Init the unload section of the module. */ static void module_unload_init(struct module *mod) { @@ -692,7 +730,7 @@ static int obsparm_copy_string(const char *val, struct kernel_param *kp) return 0; } -int set_obsolete(const char *val, struct kernel_param *kp) +static int set_obsolete(const char *val, struct kernel_param *kp) { unsigned int min, max; unsigned int size, maxsize; @@ -1031,6 +1069,32 @@ static void module_remove_refcnt_attr(struct module *mod) } #endif +#ifdef CONFIG_MODULE_UNLOAD +static int module_add_modinfo_attrs(struct module *mod) +{ + struct module_attribute *attr; + int error = 0; + int i; + + for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { + if (!attr->test || + (attr->test && attr->test(mod))) + error = sysfs_create_file(&mod->mkobj.kobj,&attr->attr); + } + return error; +} + +static void module_remove_modinfo_attrs(struct module *mod) +{ + struct module_attribute *attr; + int i; + + for (i = 0; (attr = modinfo_attrs[i]); i++) { + sysfs_remove_file(&mod->mkobj.kobj,&attr->attr); + attr->free(mod); + } +} +#endif static int mod_sysfs_setup(struct module *mod, struct kernel_param *kparam, @@ -1056,6 +1120,12 @@ static int mod_sysfs_setup(struct module *mod, if (err) goto out_unreg; +#ifdef CONFIG_MODULE_UNLOAD + err = module_add_modinfo_attrs(mod); + if (err) + goto out_unreg; +#endif + return 0; out_unreg: @@ -1066,6 +1136,9 @@ out: static void mod_kobject_remove(struct module *mod) { +#ifdef CONFIG_MODULE_UNLOAD + module_remove_modinfo_attrs(mod); +#endif module_remove_refcnt_attr(mod); module_param_sysfs_remove(mod); @@ -1311,6 +1384,23 @@ static char *get_modinfo(Elf_Shdr *sechdrs, return NULL; } +#ifdef CONFIG_MODULE_UNLOAD +static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs, + unsigned int infoindex) +{ + struct module_attribute *attr; + int i; + + for (i = 0; (attr = modinfo_attrs[i]); i++) { + if (attr->setup) + attr->setup(mod, + get_modinfo(sechdrs, + infoindex, + attr->attr.name)); + } +} +#endif + #ifdef CONFIG_KALLSYMS int is_exported(const char *name, const struct module *mod) { @@ -1615,6 +1705,11 @@ static struct module *load_module(void __user *umod, /* Set up license info based on the info section */ set_license(mod, get_modinfo(sechdrs, infoindex, "license")); +#ifdef CONFIG_MODULE_UNLOAD + /* Set up MODINFO_ATTR fields */ + setup_modinfo(mod, sechdrs, infoindex); +#endif + /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, mod); diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index cabb63fc9e16..5b7b4736d82b 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -88,23 +88,6 @@ static kmem_cache_t *posix_timers_cache; static struct idr posix_timers_id; static DEFINE_SPINLOCK(idr_lock); -/* - * Just because the timer is not in the timer list does NOT mean it is - * inactive. It could be in the "fire" routine getting a new expire time. - */ -#define TIMER_INACTIVE 1 - -#ifdef CONFIG_SMP -# define timer_active(tmr) \ - ((tmr)->it.real.timer.entry.prev != (void *)TIMER_INACTIVE) -# define set_timer_inactive(tmr) \ - do { \ - (tmr)->it.real.timer.entry.prev = (void *)TIMER_INACTIVE; \ - } while (0) -#else -# define timer_active(tmr) BARFY // error to use outside of SMP -# define set_timer_inactive(tmr) do { } while (0) -#endif /* * we assume that the new SIGEV_THREAD_ID shares no bits with the other * SIGEV values. Here we put out an error if this assumption fails. @@ -226,7 +209,6 @@ static inline int common_timer_create(struct k_itimer *new_timer) init_timer(&new_timer->it.real.timer); new_timer->it.real.timer.data = (unsigned long) new_timer; new_timer->it.real.timer.function = posix_timer_fn; - set_timer_inactive(new_timer); return 0; } @@ -480,7 +462,6 @@ static void posix_timer_fn(unsigned long __data) int do_notify = 1; spin_lock_irqsave(&timr->it_lock, flags); - set_timer_inactive(timr); if (!list_empty(&timr->it.real.abs_timer_entry)) { spin_lock(&abs_list.lock); do { @@ -983,8 +964,8 @@ common_timer_set(struct k_itimer *timr, int flags, * careful here. If smp we could be in the "fire" routine which will * be spinning as we hold the lock. But this is ONLY an SMP issue. */ + if (try_to_del_timer_sync(&timr->it.real.timer) < 0) { #ifdef CONFIG_SMP - if (timer_active(timr) && !del_timer(&timr->it.real.timer)) /* * It can only be active if on an other cpu. Since * we have cleared the interval stuff above, it should @@ -994,11 +975,9 @@ common_timer_set(struct k_itimer *timr, int flags, * a "retry" exit status. */ return TIMER_RETRY; - - set_timer_inactive(timr); -#else - del_timer(&timr->it.real.timer); #endif + } + remove_from_abslist(timr); timr->it_requeue_pending = (timr->it_requeue_pending + 2) & @@ -1083,8 +1062,9 @@ retry: static inline int common_timer_del(struct k_itimer *timer) { timer->it.real.incr = 0; + + if (try_to_del_timer_sync(&timer->it.real.timer) < 0) { #ifdef CONFIG_SMP - if (timer_active(timer) && !del_timer(&timer->it.real.timer)) /* * It can only be active if on an other cpu. Since * we have cleared the interval stuff above, it should @@ -1094,9 +1074,9 @@ static inline int common_timer_del(struct k_itimer *timer) * a "retry" exit status. */ return TIMER_RETRY; -#else - del_timer(&timer->it.real.timer); #endif + } + remove_from_abslist(timer); return 0; diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 90b3b68dee3f..53f9f8720ee4 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -81,7 +81,7 @@ static int nr_copy_pages_check; extern char resume_file[]; /* Local variables that should not be affected by save */ -unsigned int nr_copy_pages __nosavedata = 0; +static unsigned int nr_copy_pages __nosavedata = 0; /* Suspend pagedir is allocated before final copy, therefore it must be freed after resume diff --git a/kernel/printk.c b/kernel/printk.c index 01b58d7d17ff..3a442bfb8bee 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -876,8 +876,10 @@ void register_console(struct console * console) break; console->flags |= CON_ENABLED; console->index = console_cmdline[i].index; - if (i == preferred_console) + if (i == selected_console) { console->flags |= CON_CONSDEV; + preferred_console = selected_console; + } break; } @@ -897,6 +899,8 @@ void register_console(struct console * console) if ((console->flags & CON_CONSDEV) || console_drivers == NULL) { console->next = console_drivers; console_drivers = console; + if (console->next) + console->next->flags &= ~CON_CONSDEV; } else { console->next = console_drivers->next; console_drivers->next = console; @@ -937,10 +941,14 @@ int unregister_console(struct console * console) /* If last console is removed, we re-enable picking the first * one that gets registered. Without that, pmac early boot console * would prevent fbcon from taking over. + * + * If this isn't the last console and it has CON_CONSDEV set, we + * need to set it on the next preferred console. */ if (console_drivers == NULL) preferred_console = selected_console; - + else if (console->flags & CON_CONSDEV) + console_drivers->flags |= CON_CONSDEV; release_console_sem(); return res; diff --git a/kernel/sched.c b/kernel/sched.c index deca041fc364..76080d142e3d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2576,7 +2576,7 @@ void fastcall add_preempt_count(int val) /* * Underflow? */ - BUG_ON(((int)preempt_count() < 0)); + BUG_ON((preempt_count() < 0)); preempt_count() += val; /* * Spinlock count overflowing soon? @@ -2869,7 +2869,7 @@ need_resched: int default_wake_function(wait_queue_t *curr, unsigned mode, int sync, void *key) { - task_t *p = curr->task; + task_t *p = curr->private; return try_to_wake_up(p, mode, sync); } diff --git a/kernel/signal.c b/kernel/signal.c index c89821b69ae3..d1258729a5f9 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -213,6 +213,7 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) fastcall void recalc_sigpending_tsk(struct task_struct *t) { if (t->signal->group_stop_count > 0 || + (t->flags & PF_FREEZE) || PENDING(&t->pending, &t->blocked) || PENDING(&t->signal->shared_pending, &t->blocked)) set_tsk_thread_flag(t, TIF_SIGPENDING); diff --git a/kernel/sys.c b/kernel/sys.c index f006632c2ba7..da24bc1292db 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -525,7 +525,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) } if (new_egid != old_egid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } if (rgid != (gid_t) -1 || @@ -556,7 +556,7 @@ asmlinkage long sys_setgid(gid_t gid) { if(old_egid != gid) { - current->mm->dumpable=0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->gid = current->egid = current->sgid = current->fsgid = gid; @@ -565,7 +565,7 @@ asmlinkage long sys_setgid(gid_t gid) { if(old_egid != gid) { - current->mm->dumpable=0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->egid = current->fsgid = gid; @@ -596,7 +596,7 @@ static int set_user(uid_t new_ruid, int dumpclear) if(dumpclear) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->uid = new_ruid; @@ -653,7 +653,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) if (new_euid != old_euid) { - current->mm->dumpable=0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = current->euid = new_euid; @@ -703,7 +703,7 @@ asmlinkage long sys_setuid(uid_t uid) if (old_euid != uid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = current->euid = uid; @@ -748,7 +748,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) if (euid != (uid_t) -1) { if (euid != current->euid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->euid = euid; @@ -798,7 +798,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) if (egid != (gid_t) -1) { if (egid != current->egid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->egid = egid; @@ -845,7 +845,7 @@ asmlinkage long sys_setfsuid(uid_t uid) { if (uid != old_fsuid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = uid; @@ -875,7 +875,7 @@ asmlinkage long sys_setfsgid(gid_t gid) { if (gid != old_fsgid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsgid = gid; @@ -894,35 +894,69 @@ asmlinkage long sys_times(struct tms __user * tbuf) */ if (tbuf) { struct tms tmp; - struct task_struct *tsk = current; - struct task_struct *t; cputime_t utime, stime, cutime, cstime; - read_lock(&tasklist_lock); - utime = tsk->signal->utime; - stime = tsk->signal->stime; - t = tsk; - do { - utime = cputime_add(utime, t->utime); - stime = cputime_add(stime, t->stime); - t = next_thread(t); - } while (t != tsk); +#ifdef CONFIG_SMP + if (thread_group_empty(current)) { + /* + * Single thread case without the use of any locks. + * + * We may race with release_task if two threads are + * executing. However, release task first adds up the + * counters (__exit_signal) before removing the task + * from the process tasklist (__unhash_process). + * __exit_signal also acquires and releases the + * siglock which results in the proper memory ordering + * so that the list modifications are always visible + * after the counters have been updated. + * + * If the counters have been updated by the second thread + * but the thread has not yet been removed from the list + * then the other branch will be executing which will + * block on tasklist_lock until the exit handling of the + * other task is finished. + * + * This also implies that the sighand->siglock cannot + * be held by another processor. So we can also + * skip acquiring that lock. + */ + utime = cputime_add(current->signal->utime, current->utime); + stime = cputime_add(current->signal->utime, current->stime); + cutime = current->signal->cutime; + cstime = current->signal->cstime; + } else +#endif + { - /* - * While we have tasklist_lock read-locked, no dying thread - * can be updating current->signal->[us]time. Instead, - * we got their counts included in the live thread loop. - * However, another thread can come in right now and - * do a wait call that updates current->signal->c[us]time. - * To make sure we always see that pair updated atomically, - * we take the siglock around fetching them. - */ - spin_lock_irq(&tsk->sighand->siglock); - cutime = tsk->signal->cutime; - cstime = tsk->signal->cstime; - spin_unlock_irq(&tsk->sighand->siglock); - read_unlock(&tasklist_lock); + /* Process with multiple threads */ + struct task_struct *tsk = current; + struct task_struct *t; + read_lock(&tasklist_lock); + utime = tsk->signal->utime; + stime = tsk->signal->stime; + t = tsk; + do { + utime = cputime_add(utime, t->utime); + stime = cputime_add(stime, t->stime); + t = next_thread(t); + } while (t != tsk); + + /* + * While we have tasklist_lock read-locked, no dying thread + * can be updating current->signal->[us]time. Instead, + * we got their counts included in the live thread loop. + * However, another thread can come in right now and + * do a wait call that updates current->signal->c[us]time. + * To make sure we always see that pair updated atomically, + * we take the siglock around fetching them. + */ + spin_lock_irq(&tsk->sighand->siglock); + cutime = tsk->signal->cutime; + cstime = tsk->signal->cstime; + spin_unlock_irq(&tsk->sighand->siglock); + read_unlock(&tasklist_lock); + } tmp.tms_utime = cputime_to_clock_t(utime); tmp.tms_stime = cputime_to_clock_t(stime); tmp.tms_cutime = cputime_to_clock_t(cutime); @@ -1225,7 +1259,7 @@ static void groups_sort(struct group_info *group_info) } /* a simple bsearch */ -static int groups_search(struct group_info *group_info, gid_t grp) +int groups_search(struct group_info *group_info, gid_t grp) { int left, right; @@ -1652,7 +1686,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, error = 1; break; case PR_SET_DUMPABLE: - if (arg2 != 0 && arg2 != 1) { + if (arg2 < 0 || arg2 > 2) { error = -EINVAL; break; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 701d12c63068..24a4d12d5aa9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -58,6 +58,7 @@ extern int sysctl_overcommit_ratio; extern int max_threads; extern int sysrq_enabled; extern int core_uses_pid; +extern int suid_dumpable; extern char core_pattern[]; extern int cad_pid; extern int pid_max; @@ -950,6 +951,14 @@ static ctl_table fs_table[] = { .proc_handler = &proc_dointvec, }, #endif + { + .ctl_name = KERN_SETUID_DUMPABLE, + .procname = "suid_dumpable", + .data = &suid_dumpable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0 } }; diff --git a/kernel/timer.c b/kernel/timer.c index 207aa4f0aa10..51ff917c9590 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -57,6 +57,11 @@ static void time_interpolator_update(long delta_nsec); #define TVN_MASK (TVN_SIZE - 1) #define TVR_MASK (TVR_SIZE - 1) +struct timer_base_s { + spinlock_t lock; + struct timer_list *running_timer; +}; + typedef struct tvec_s { struct list_head vec[TVN_SIZE]; } tvec_t; @@ -66,9 +71,8 @@ typedef struct tvec_root_s { } tvec_root_t; struct tvec_t_base_s { - spinlock_t lock; + struct timer_base_s t_base; unsigned long timer_jiffies; - struct timer_list *running_timer; tvec_root_t tv1; tvec_t tv2; tvec_t tv3; @@ -77,18 +81,16 @@ struct tvec_t_base_s { } ____cacheline_aligned_in_smp; typedef struct tvec_t_base_s tvec_base_t; +static DEFINE_PER_CPU(tvec_base_t, tvec_bases); static inline void set_running_timer(tvec_base_t *base, struct timer_list *timer) { #ifdef CONFIG_SMP - base->running_timer = timer; + base->t_base.running_timer = timer; #endif } -/* Fake initialization */ -static DEFINE_PER_CPU(tvec_base_t, tvec_bases) = { SPIN_LOCK_UNLOCKED }; - static void check_timer_failed(struct timer_list *timer) { static int whine_count; @@ -103,7 +105,6 @@ static void check_timer_failed(struct timer_list *timer) /* * Now fix it up */ - spin_lock_init(&timer->lock); timer->magic = TIMER_MAGIC; } @@ -156,65 +157,113 @@ static void internal_add_timer(tvec_base_t *base, struct timer_list *timer) list_add_tail(&timer->entry, vec); } +typedef struct timer_base_s timer_base_t; +/* + * Used by TIMER_INITIALIZER, we can't use per_cpu(tvec_bases) + * at compile time, and we need timer->base to lock the timer. + */ +timer_base_t __init_timer_base + ____cacheline_aligned_in_smp = { .lock = SPIN_LOCK_UNLOCKED }; +EXPORT_SYMBOL(__init_timer_base); + +/*** + * init_timer - initialize a timer. + * @timer: the timer to be initialized + * + * init_timer() must be done to a timer prior calling *any* of the + * other timer functions. + */ +void fastcall init_timer(struct timer_list *timer) +{ + timer->entry.next = NULL; + timer->base = &per_cpu(tvec_bases, raw_smp_processor_id()).t_base; + timer->magic = TIMER_MAGIC; +} +EXPORT_SYMBOL(init_timer); + +static inline void detach_timer(struct timer_list *timer, + int clear_pending) +{ + struct list_head *entry = &timer->entry; + + __list_del(entry->prev, entry->next); + if (clear_pending) + entry->next = NULL; + entry->prev = LIST_POISON2; +} + +/* + * We are using hashed locking: holding per_cpu(tvec_bases).t_base.lock + * means that all timers which are tied to this base via timer->base are + * locked, and the base itself is locked too. + * + * So __run_timers/migrate_timers can safely modify all timers which could + * be found on ->tvX lists. + * + * When the timer's base is locked, and the timer removed from list, it is + * possible to set timer->base = NULL and drop the lock: the timer remains + * locked. + */ +static timer_base_t *lock_timer_base(struct timer_list *timer, + unsigned long *flags) +{ + timer_base_t *base; + + for (;;) { + base = timer->base; + if (likely(base != NULL)) { + spin_lock_irqsave(&base->lock, *flags); + if (likely(base == timer->base)) + return base; + /* The timer has migrated to another CPU */ + spin_unlock_irqrestore(&base->lock, *flags); + } + cpu_relax(); + } +} + int __mod_timer(struct timer_list *timer, unsigned long expires) { - tvec_base_t *old_base, *new_base; + timer_base_t *base; + tvec_base_t *new_base; unsigned long flags; int ret = 0; BUG_ON(!timer->function); - check_timer(timer); - spin_lock_irqsave(&timer->lock, flags); - new_base = &__get_cpu_var(tvec_bases); -repeat: - old_base = timer->base; + base = lock_timer_base(timer, &flags); - /* - * Prevent deadlocks via ordering by old_base < new_base. - */ - if (old_base && (new_base != old_base)) { - if (old_base < new_base) { - spin_lock(&new_base->lock); - spin_lock(&old_base->lock); - } else { - spin_lock(&old_base->lock); - spin_lock(&new_base->lock); - } - /* - * The timer base might have been cancelled while we were - * trying to take the lock(s): - */ - if (timer->base != old_base) { - spin_unlock(&new_base->lock); - spin_unlock(&old_base->lock); - goto repeat; - } - } else { - spin_lock(&new_base->lock); - if (timer->base != old_base) { - spin_unlock(&new_base->lock); - goto repeat; - } - } - - /* - * Delete the previous timeout (if there was any), and install - * the new one: - */ - if (old_base) { - list_del(&timer->entry); + if (timer_pending(timer)) { + detach_timer(timer, 0); ret = 1; } + + new_base = &__get_cpu_var(tvec_bases); + + if (base != &new_base->t_base) { + /* + * We are trying to schedule the timer on the local CPU. + * However we can't change timer's base while it is running, + * otherwise del_timer_sync() can't detect that the timer's + * handler yet has not finished. This also guarantees that + * the timer is serialized wrt itself. + */ + if (unlikely(base->running_timer == timer)) { + /* The timer remains on a former base */ + new_base = container_of(base, tvec_base_t, t_base); + } else { + /* See the comment in lock_timer_base() */ + timer->base = NULL; + spin_unlock(&base->lock); + spin_lock(&new_base->t_base.lock); + timer->base = &new_base->t_base; + } + } + timer->expires = expires; internal_add_timer(new_base, timer); - timer->base = new_base; - - if (old_base && (new_base != old_base)) - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); - spin_unlock_irqrestore(&timer->lock, flags); + spin_unlock_irqrestore(&new_base->t_base.lock, flags); return ret; } @@ -232,15 +281,15 @@ void add_timer_on(struct timer_list *timer, int cpu) { tvec_base_t *base = &per_cpu(tvec_bases, cpu); unsigned long flags; - + BUG_ON(timer_pending(timer) || !timer->function); check_timer(timer); - spin_lock_irqsave(&base->lock, flags); + spin_lock_irqsave(&base->t_base.lock, flags); + timer->base = &base->t_base; internal_add_timer(base, timer); - timer->base = base; - spin_unlock_irqrestore(&base->lock, flags); + spin_unlock_irqrestore(&base->t_base.lock, flags); } @@ -295,32 +344,55 @@ EXPORT_SYMBOL(mod_timer); */ int del_timer(struct timer_list *timer) { + timer_base_t *base; unsigned long flags; - tvec_base_t *base; + int ret = 0; check_timer(timer); -repeat: - base = timer->base; - if (!base) - return 0; - spin_lock_irqsave(&base->lock, flags); - if (base != timer->base) { + if (timer_pending(timer)) { + base = lock_timer_base(timer, &flags); + if (timer_pending(timer)) { + detach_timer(timer, 1); + ret = 1; + } spin_unlock_irqrestore(&base->lock, flags); - goto repeat; } - list_del(&timer->entry); - /* Need to make sure that anybody who sees a NULL base also sees the list ops */ - smp_wmb(); - timer->base = NULL; - spin_unlock_irqrestore(&base->lock, flags); - return 1; + return ret; } EXPORT_SYMBOL(del_timer); #ifdef CONFIG_SMP +/* + * This function tries to deactivate a timer. Upon successful (ret >= 0) + * exit the timer is not queued and the handler is not running on any CPU. + * + * It must not be called from interrupt contexts. + */ +int try_to_del_timer_sync(struct timer_list *timer) +{ + timer_base_t *base; + unsigned long flags; + int ret = -1; + + base = lock_timer_base(timer, &flags); + + if (base->running_timer == timer) + goto out; + + ret = 0; + if (timer_pending(timer)) { + detach_timer(timer, 1); + ret = 1; + } +out: + spin_unlock_irqrestore(&base->lock, flags); + + return ret; +} + /*** * del_timer_sync - deactivate a timer and wait for the handler to finish. * @timer: the timer to be deactivated @@ -332,72 +404,24 @@ EXPORT_SYMBOL(del_timer); * Synchronization rules: callers must prevent restarting of the timer, * otherwise this function is meaningless. It must not be called from * interrupt contexts. The caller must not hold locks which would prevent - * completion of the timer's handler. Upon exit the timer is not queued and - * the handler is not running on any CPU. + * completion of the timer's handler. The timer's handler must not call + * add_timer_on(). Upon exit the timer is not queued and the handler is + * not running on any CPU. * * The function returns whether it has deactivated a pending timer or not. - * - * del_timer_sync() is slow and complicated because it copes with timer - * handlers which re-arm the timer (periodic timers). If the timer handler - * is known to not do this (a single shot timer) then use - * del_singleshot_timer_sync() instead. */ int del_timer_sync(struct timer_list *timer) { - tvec_base_t *base; - int i, ret = 0; - check_timer(timer); -del_again: - ret += del_timer(timer); - - for_each_online_cpu(i) { - base = &per_cpu(tvec_bases, i); - if (base->running_timer == timer) { - while (base->running_timer == timer) { - cpu_relax(); - preempt_check_resched(); - } - break; - } + for (;;) { + int ret = try_to_del_timer_sync(timer); + if (ret >= 0) + return ret; } - smp_rmb(); - if (timer_pending(timer)) - goto del_again; - - return ret; } + EXPORT_SYMBOL(del_timer_sync); - -/*** - * del_singleshot_timer_sync - deactivate a non-recursive timer - * @timer: the timer to be deactivated - * - * This function is an optimization of del_timer_sync for the case where the - * caller can guarantee the timer does not reschedule itself in its timer - * function. - * - * Synchronization rules: callers must prevent restarting of the timer, - * otherwise this function is meaningless. It must not be called from - * interrupt contexts. The caller must not hold locks which wold prevent - * completion of the timer's handler. Upon exit the timer is not queued and - * the handler is not running on any CPU. - * - * The function returns whether it has deactivated a pending timer or not. - */ -int del_singleshot_timer_sync(struct timer_list *timer) -{ - int ret = del_timer(timer); - - if (!ret) { - ret = del_timer_sync(timer); - BUG_ON(ret); - } - - return ret; -} -EXPORT_SYMBOL(del_singleshot_timer_sync); #endif static int cascade(tvec_base_t *base, tvec_t *tv, int index) @@ -415,7 +439,7 @@ static int cascade(tvec_base_t *base, tvec_t *tv, int index) struct timer_list *tmp; tmp = list_entry(curr, struct timer_list, entry); - BUG_ON(tmp->base != base); + BUG_ON(tmp->base != &base->t_base); curr = curr->next; internal_add_timer(base, tmp); } @@ -437,7 +461,7 @@ static inline void __run_timers(tvec_base_t *base) { struct timer_list *timer; - spin_lock_irq(&base->lock); + spin_lock_irq(&base->t_base.lock); while (time_after_eq(jiffies, base->timer_jiffies)) { struct list_head work_list = LIST_HEAD_INIT(work_list); struct list_head *head = &work_list; @@ -453,8 +477,7 @@ static inline void __run_timers(tvec_base_t *base) cascade(base, &base->tv5, INDEX(3)); ++base->timer_jiffies; list_splice_init(base->tv1.vec + index, &work_list); -repeat: - if (!list_empty(head)) { + while (!list_empty(head)) { void (*fn)(unsigned long); unsigned long data; @@ -462,25 +485,26 @@ repeat: fn = timer->function; data = timer->data; - list_del(&timer->entry); set_running_timer(base, timer); - smp_wmb(); - timer->base = NULL; - spin_unlock_irq(&base->lock); + detach_timer(timer, 1); + spin_unlock_irq(&base->t_base.lock); { - u32 preempt_count = preempt_count(); + int preempt_count = preempt_count(); fn(data); if (preempt_count != preempt_count()) { - printk("huh, entered %p with %08x, exited with %08x?\n", fn, preempt_count, preempt_count()); + printk(KERN_WARNING "huh, entered %p " + "with preempt_count %08x, exited" + " with %08x?\n", + fn, preempt_count, + preempt_count()); BUG(); } } - spin_lock_irq(&base->lock); - goto repeat; + spin_lock_irq(&base->t_base.lock); } } set_running_timer(base, NULL); - spin_unlock_irq(&base->lock); + spin_unlock_irq(&base->t_base.lock); } #ifdef CONFIG_NO_IDLE_HZ @@ -499,7 +523,7 @@ unsigned long next_timer_interrupt(void) int i, j; base = &__get_cpu_var(tvec_bases); - spin_lock(&base->lock); + spin_lock(&base->t_base.lock); expires = base->timer_jiffies + (LONG_MAX >> 1); list = 0; @@ -547,7 +571,7 @@ found: expires = nte->expires; } } - spin_unlock(&base->lock); + spin_unlock(&base->t_base.lock); return expires; } #endif @@ -1286,9 +1310,9 @@ static void __devinit init_timers_cpu(int cpu) { int j; tvec_base_t *base; - + base = &per_cpu(tvec_bases, cpu); - spin_lock_init(&base->lock); + spin_lock_init(&base->t_base.lock); for (j = 0; j < TVN_SIZE; j++) { INIT_LIST_HEAD(base->tv5.vec + j); INIT_LIST_HEAD(base->tv4.vec + j); @@ -1302,22 +1326,16 @@ static void __devinit init_timers_cpu(int cpu) } #ifdef CONFIG_HOTPLUG_CPU -static int migrate_timer_list(tvec_base_t *new_base, struct list_head *head) +static void migrate_timer_list(tvec_base_t *new_base, struct list_head *head) { struct timer_list *timer; while (!list_empty(head)) { timer = list_entry(head->next, struct timer_list, entry); - /* We're locking backwards from __mod_timer order here, - beware deadlock. */ - if (!spin_trylock(&timer->lock)) - return 0; - list_del(&timer->entry); + detach_timer(timer, 0); + timer->base = &new_base->t_base; internal_add_timer(new_base, timer); - timer->base = new_base; - spin_unlock(&timer->lock); } - return 1; } static void __devinit migrate_timers(int cpu) @@ -1331,39 +1349,24 @@ static void __devinit migrate_timers(int cpu) new_base = &get_cpu_var(tvec_bases); local_irq_disable(); -again: - /* Prevent deadlocks via ordering by old_base < new_base. */ - if (old_base < new_base) { - spin_lock(&new_base->lock); - spin_lock(&old_base->lock); - } else { - spin_lock(&old_base->lock); - spin_lock(&new_base->lock); - } + spin_lock(&new_base->t_base.lock); + spin_lock(&old_base->t_base.lock); - if (old_base->running_timer) + if (old_base->t_base.running_timer) BUG(); for (i = 0; i < TVR_SIZE; i++) - if (!migrate_timer_list(new_base, old_base->tv1.vec + i)) - goto unlock_again; - for (i = 0; i < TVN_SIZE; i++) - if (!migrate_timer_list(new_base, old_base->tv2.vec + i) - || !migrate_timer_list(new_base, old_base->tv3.vec + i) - || !migrate_timer_list(new_base, old_base->tv4.vec + i) - || !migrate_timer_list(new_base, old_base->tv5.vec + i)) - goto unlock_again; - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); + migrate_timer_list(new_base, old_base->tv1.vec + i); + for (i = 0; i < TVN_SIZE; i++) { + migrate_timer_list(new_base, old_base->tv2.vec + i); + migrate_timer_list(new_base, old_base->tv3.vec + i); + migrate_timer_list(new_base, old_base->tv4.vec + i); + migrate_timer_list(new_base, old_base->tv5.vec + i); + } + + spin_unlock(&old_base->t_base.lock); + spin_unlock(&new_base->t_base.lock); local_irq_enable(); put_cpu_var(tvec_bases); - return; - -unlock_again: - /* Avoid deadlock with __mod_timer, by backing off. */ - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); - cpu_relax(); - goto again; } #endif /* CONFIG_HOTPLUG_CPU */ diff --git a/lib/Kconfig b/lib/Kconfig index 2d4d4e3bc4aa..455833a9e31a 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -63,5 +63,32 @@ config REED_SOLOMON_ENC16 config REED_SOLOMON_DEC16 boolean -endmenu +config TEXTSEARCH + boolean "Textsearch infrastructure" + default y + help + Say Y here if you want to provide a textsearch infrastructure + to other subsystems. +config TEXTSEARCH_KMP + depends on TEXTSEARCH + tristate "Knuth-Morris-Pratt" + help + Say Y here if you want to be able to search text using the + Knuth-Morris-Pratt textsearch algorithm. + + To compile this code as a module, choose M here: the + module will be called ts_kmp. + +config TEXTSEARCH_FSM + depends on TEXTSEARCH + tristate "Finite state machine" + help + Say Y here if you want to be able to search text using a + naive finite state machine approach implementing a subset + of regular expressions. + + To compile this code as a module, choose M here: the + module will be called ts_fsm. + +endmenu diff --git a/lib/Makefile b/lib/Makefile index dcb4231916e2..beed1585294c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -36,6 +36,10 @@ obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/ obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ +obj-$(CONFIG_TEXTSEARCH) += textsearch.o +obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o +obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/textsearch.c b/lib/textsearch.c new file mode 100644 index 000000000000..1e934c196f0f --- /dev/null +++ b/lib/textsearch.c @@ -0,0 +1,317 @@ +/* + * lib/textsearch.c Generic text search interface + * + * 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: Thomas Graf + * Pablo Neira Ayuso + * + * ========================================================================== + * + * INTRODUCTION + * + * The textsearch infrastructure provides text searching facitilies for + * both linear and non-linear data. Individual search algorithms are + * implemented in modules and chosen by the user. + * + * ARCHITECTURE + * + * User + * +----------------+ + * | finish()|<--------------(6)-----------------+ + * |get_next_block()|<--------------(5)---------------+ | + * | | Algorithm | | + * | | +------------------------------+ + * | | | init() find() destroy() | + * | | +------------------------------+ + * | | Core API ^ ^ ^ + * | | +---------------+ (2) (4) (8) + * | (1)|----->| prepare() |---+ | | + * | (3)|----->| find()/next() |-----------+ | + * | (7)|----->| destroy() |----------------------+ + * +----------------+ +---------------+ + * + * (1) User configures a search by calling _prepare() specifying the + * search parameters such as the pattern and algorithm name. + * (2) Core requests the algorithm to allocate and initialize a search + * configuration according to the specified parameters. + * (3) User starts the search(es) by calling _find() or _next() to + * fetch subsequent occurrences. A state variable is provided + * to the algorihtm to store persistant variables. + * (4) Core eventually resets the search offset and forwards the find() + * request to the algorithm. + * (5) Algorithm calls get_next_block() provided by the user continously + * to fetch the data to be searched in block by block. + * (6) Algorithm invokes finish() after the last call to get_next_block + * to clean up any leftovers from get_next_block. (Optional) + * (7) User destroys the configuration by calling _destroy(). + * (8) Core notifies the algorithm to destroy algorithm specific + * allocations. (Optional) + * + * USAGE + * + * Before a search can be performed, a configuration must be created + * by calling textsearch_prepare() specyfing the searching algorithm and + * the pattern to look for. The returned configuration may then be used + * for an arbitary amount of times and even in parallel as long as a + * separate struct ts_state variable is provided to every instance. + * + * The actual search is performed by either calling textsearch_find_- + * continuous() for linear data or by providing an own get_next_block() + * implementation and calling textsearch_find(). Both functions return + * the position of the first occurrence of the patern or UINT_MAX if + * no match was found. Subsequent occurences can be found by calling + * textsearch_next() regardless of the linearity of the data. + * + * Once you're done using a configuration it must be given back via + * textsearch_destroy. + * + * EXAMPLE + * + * int pos; + * struct ts_config *conf; + * struct ts_state state; + * const char *pattern = "chicken"; + * const char *example = "We dance the funky chicken"; + * + * conf = textsearch_prepare("kmp", pattern, strlen(pattern), + * GFP_KERNEL, TS_AUTOLOAD); + * if (IS_ERR(conf)) { + * err = PTR_ERR(conf); + * goto errout; + * } + * + * pos = textsearch_find_continuous(conf, &state, example, strlen(example)); + * if (pos != UINT_MAX) + * panic("Oh my god, dancing chickens at %d\n", pos); + * + * textsearch_destroy(conf); + * + * ========================================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(ts_ops); +static DEFINE_SPINLOCK(ts_mod_lock); + +static inline struct ts_ops *lookup_ts_algo(const char *name) +{ + struct ts_ops *o; + + rcu_read_lock(); + list_for_each_entry_rcu(o, &ts_ops, list) { + if (!strcmp(name, o->name)) { + if (!try_module_get(o->owner)) + o = NULL; + rcu_read_unlock(); + return o; + } + } + rcu_read_unlock(); + + return NULL; +} + +/** + * textsearch_register - register a textsearch module + * @ops: operations lookup table + * + * This function must be called by textsearch modules to announce + * their presence. The specified &@ops must have %name set to a + * unique identifier and the callbacks find(), init(), get_pattern(), + * and get_pattern_len() must be implemented. + * + * Returns 0 or -EEXISTS if another module has already registered + * with same name. + */ +int textsearch_register(struct ts_ops *ops) +{ + int err = -EEXIST; + struct ts_ops *o; + + if (ops->name == NULL || ops->find == NULL || ops->init == NULL || + ops->get_pattern == NULL || ops->get_pattern_len == NULL) + return -EINVAL; + + spin_lock(&ts_mod_lock); + list_for_each_entry(o, &ts_ops, list) { + if (!strcmp(ops->name, o->name)) + goto errout; + } + + list_add_tail_rcu(&ops->list, &ts_ops); + err = 0; +errout: + spin_unlock(&ts_mod_lock); + return err; +} + +/** + * textsearch_unregister - unregister a textsearch module + * @ops: operations lookup table + * + * This function must be called by textsearch modules to announce + * their disappearance for examples when the module gets unloaded. + * The &ops parameter must be the same as the one during the + * registration. + * + * Returns 0 on success or -ENOENT if no matching textsearch + * registration was found. + */ +int textsearch_unregister(struct ts_ops *ops) +{ + int err = 0; + struct ts_ops *o; + + spin_lock(&ts_mod_lock); + list_for_each_entry(o, &ts_ops, list) { + if (o == ops) { + list_del_rcu(&o->list); + goto out; + } + } + + err = -ENOENT; +out: + spin_unlock(&ts_mod_lock); + return err; +} + +struct ts_linear_state +{ + unsigned int len; + const void *data; +}; + +static unsigned int get_linear_data(unsigned int consumed, const u8 **dst, + struct ts_config *conf, + struct ts_state *state) +{ + struct ts_linear_state *st = (struct ts_linear_state *) state->cb; + + if (likely(consumed < st->len)) { + *dst = st->data + consumed; + return st->len - consumed; + } + + return 0; +} + +/** + * textsearch_find_continuous - search a pattern in continuous/linear data + * @conf: search configuration + * @state: search state + * @data: data to search in + * @len: length of data + * + * A simplified version of textsearch_find() for continuous/linear data. + * Call textsearch_next() to retrieve subsequent matches. + * + * Returns the position of first occurrence of the pattern or + * UINT_MAX if no occurrence was found. + */ +unsigned int textsearch_find_continuous(struct ts_config *conf, + struct ts_state *state, + const void *data, unsigned int len) +{ + struct ts_linear_state *st = (struct ts_linear_state *) state->cb; + + conf->get_next_block = get_linear_data; + st->data = data; + st->len = len; + + return textsearch_find(conf, state); +} + +/** + * textsearch_prepare - Prepare a search + * @algo: name of search algorithm + * @pattern: pattern data + * @len: length of pattern + * @gfp_mask: allocation mask + * @flags: search flags + * + * Looks up the search algorithm module and creates a new textsearch + * configuration for the specified pattern. Upon completion all + * necessary refcnts are held and the configuration must be put back + * using textsearch_put() after usage. + * + * Note: The format of the pattern may not be compatible between + * the various search algorithms. + * + * Returns a new textsearch configuration according to the specified + * parameters or a ERR_PTR(). + */ +struct ts_config *textsearch_prepare(const char *algo, const void *pattern, + unsigned int len, int gfp_mask, int flags) +{ + int err = -ENOENT; + struct ts_config *conf; + struct ts_ops *ops; + + ops = lookup_ts_algo(algo); +#ifdef CONFIG_KMOD + /* + * Why not always autoload you may ask. Some users are + * in a situation where requesting a module may deadlock, + * especially when the module is located on a NFS mount. + */ + if (ops == NULL && flags & TS_AUTOLOAD) { + request_module("ts_%s", algo); + ops = lookup_ts_algo(algo); + } +#endif + + if (ops == NULL) + goto errout; + + conf = ops->init(pattern, len, gfp_mask); + if (IS_ERR(conf)) { + err = PTR_ERR(conf); + goto errout; + } + + conf->ops = ops; + return conf; + +errout: + if (ops) + module_put(ops->owner); + + return ERR_PTR(err); +} + +/** + * textsearch_destroy - destroy a search configuration + * @conf: search configuration + * + * Releases all references of the configuration and frees + * up the memory. + */ +void textsearch_destroy(struct ts_config *conf) +{ + if (conf->ops) { + if (conf->ops->destroy) + conf->ops->destroy(conf); + module_put(conf->ops->owner); + } + + kfree(conf); +} + +EXPORT_SYMBOL(textsearch_register); +EXPORT_SYMBOL(textsearch_unregister); +EXPORT_SYMBOL(textsearch_prepare); +EXPORT_SYMBOL(textsearch_find_continuous); +EXPORT_SYMBOL(textsearch_destroy); diff --git a/lib/ts_fsm.c b/lib/ts_fsm.c new file mode 100644 index 000000000000..d27c0a072940 --- /dev/null +++ b/lib/ts_fsm.c @@ -0,0 +1,338 @@ +/* + * lib/ts_fsm.c A naive finite state machine text search approach + * + * 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: Thomas Graf + * + * ========================================================================== + * + * A finite state machine consists of n states (struct ts_fsm_token) + * representing the pattern as a finite automation. The data is read + * sequentially on a octet basis. Every state token specifies the number + * of recurrences and the type of value accepted which can be either a + * specific character or ctype based set of characters. The available + * type of recurrences include 1, (0|1), [0 n], and [1 n]. + * + * The algorithm differs between strict/non-strict mode specyfing + * whether the pattern has to start at the first octect. Strict mode + * is enabled by default and can be disabled by inserting + * TS_FSM_HEAD_IGNORE as the first token in the chain. + * + * The runtime performance of the algorithm should be around O(n), + * however while in strict mode the average runtime can be better. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct ts_fsm +{ + unsigned int ntokens; + struct ts_fsm_token tokens[0]; +}; + +/* other values derived from ctype.h */ +#define _A 0x100 /* ascii */ +#define _W 0x200 /* wildcard */ + +/* Map to _ctype flags and some magic numbers */ +static u16 token_map[TS_FSM_TYPE_MAX+1] = { + [TS_FSM_SPECIFIC] = 0, + [TS_FSM_WILDCARD] = _W, + [TS_FSM_CNTRL] = _C, + [TS_FSM_LOWER] = _L, + [TS_FSM_UPPER] = _U, + [TS_FSM_PUNCT] = _P, + [TS_FSM_SPACE] = _S, + [TS_FSM_DIGIT] = _D, + [TS_FSM_XDIGIT] = _D | _X, + [TS_FSM_ALPHA] = _U | _L, + [TS_FSM_ALNUM] = _U | _L | _D, + [TS_FSM_PRINT] = _P | _U | _L | _D | _SP, + [TS_FSM_GRAPH] = _P | _U | _L | _D, + [TS_FSM_ASCII] = _A, +}; + +static u16 token_lookup_tbl[256] = { +_W|_A|_C, _W|_A|_C, _W|_A|_C, _W|_A|_C, /* 0- 3 */ +_W|_A|_C, _W|_A|_C, _W|_A|_C, _W|_A|_C, /* 4- 7 */ +_W|_A|_C, _W|_A|_C|_S, _W|_A|_C|_S, _W|_A|_C|_S, /* 8- 11 */ +_W|_A|_C|_S, _W|_A|_C|_S, _W|_A|_C, _W|_A|_C, /* 12- 15 */ +_W|_A|_C, _W|_A|_C, _W|_A|_C, _W|_A|_C, /* 16- 19 */ +_W|_A|_C, _W|_A|_C, _W|_A|_C, _W|_A|_C, /* 20- 23 */ +_W|_A|_C, _W|_A|_C, _W|_A|_C, _W|_A|_C, /* 24- 27 */ +_W|_A|_C, _W|_A|_C, _W|_A|_C, _W|_A|_C, /* 28- 31 */ +_W|_A|_S|_SP, _W|_A|_P, _W|_A|_P, _W|_A|_P, /* 32- 35 */ +_W|_A|_P, _W|_A|_P, _W|_A|_P, _W|_A|_P, /* 36- 39 */ +_W|_A|_P, _W|_A|_P, _W|_A|_P, _W|_A|_P, /* 40- 43 */ +_W|_A|_P, _W|_A|_P, _W|_A|_P, _W|_A|_P, /* 44- 47 */ +_W|_A|_D, _W|_A|_D, _W|_A|_D, _W|_A|_D, /* 48- 51 */ +_W|_A|_D, _W|_A|_D, _W|_A|_D, _W|_A|_D, /* 52- 55 */ +_W|_A|_D, _W|_A|_D, _W|_A|_P, _W|_A|_P, /* 56- 59 */ +_W|_A|_P, _W|_A|_P, _W|_A|_P, _W|_A|_P, /* 60- 63 */ +_W|_A|_P, _W|_A|_U|_X, _W|_A|_U|_X, _W|_A|_U|_X, /* 64- 67 */ +_W|_A|_U|_X, _W|_A|_U|_X, _W|_A|_U|_X, _W|_A|_U, /* 68- 71 */ +_W|_A|_U, _W|_A|_U, _W|_A|_U, _W|_A|_U, /* 72- 75 */ +_W|_A|_U, _W|_A|_U, _W|_A|_U, _W|_A|_U, /* 76- 79 */ +_W|_A|_U, _W|_A|_U, _W|_A|_U, _W|_A|_U, /* 80- 83 */ +_W|_A|_U, _W|_A|_U, _W|_A|_U, _W|_A|_U, /* 84- 87 */ +_W|_A|_U, _W|_A|_U, _W|_A|_U, _W|_A|_P, /* 88- 91 */ +_W|_A|_P, _W|_A|_P, _W|_A|_P, _W|_A|_P, /* 92- 95 */ +_W|_A|_P, _W|_A|_L|_X, _W|_A|_L|_X, _W|_A|_L|_X, /* 96- 99 */ +_W|_A|_L|_X, _W|_A|_L|_X, _W|_A|_L|_X, _W|_A|_L, /* 100-103 */ +_W|_A|_L, _W|_A|_L, _W|_A|_L, _W|_A|_L, /* 104-107 */ +_W|_A|_L, _W|_A|_L, _W|_A|_L, _W|_A|_L, /* 108-111 */ +_W|_A|_L, _W|_A|_L, _W|_A|_L, _W|_A|_L, /* 112-115 */ +_W|_A|_L, _W|_A|_L, _W|_A|_L, _W|_A|_L, /* 116-119 */ +_W|_A|_L, _W|_A|_L, _W|_A|_L, _W|_A|_P, /* 120-123 */ +_W|_A|_P, _W|_A|_P, _W|_A|_P, _W|_A|_C, /* 124-127 */ +_W, _W, _W, _W, /* 128-131 */ +_W, _W, _W, _W, /* 132-135 */ +_W, _W, _W, _W, /* 136-139 */ +_W, _W, _W, _W, /* 140-143 */ +_W, _W, _W, _W, /* 144-147 */ +_W, _W, _W, _W, /* 148-151 */ +_W, _W, _W, _W, /* 152-155 */ +_W, _W, _W, _W, /* 156-159 */ +_W|_S|_SP, _W|_P, _W|_P, _W|_P, /* 160-163 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 164-167 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 168-171 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 172-175 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 176-179 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 180-183 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 184-187 */ +_W|_P, _W|_P, _W|_P, _W|_P, /* 188-191 */ +_W|_U, _W|_U, _W|_U, _W|_U, /* 192-195 */ +_W|_U, _W|_U, _W|_U, _W|_U, /* 196-199 */ +_W|_U, _W|_U, _W|_U, _W|_U, /* 200-203 */ +_W|_U, _W|_U, _W|_U, _W|_U, /* 204-207 */ +_W|_U, _W|_U, _W|_U, _W|_U, /* 208-211 */ +_W|_U, _W|_U, _W|_U, _W|_P, /* 212-215 */ +_W|_U, _W|_U, _W|_U, _W|_U, /* 216-219 */ +_W|_U, _W|_U, _W|_U, _W|_L, /* 220-223 */ +_W|_L, _W|_L, _W|_L, _W|_L, /* 224-227 */ +_W|_L, _W|_L, _W|_L, _W|_L, /* 228-231 */ +_W|_L, _W|_L, _W|_L, _W|_L, /* 232-235 */ +_W|_L, _W|_L, _W|_L, _W|_L, /* 236-239 */ +_W|_L, _W|_L, _W|_L, _W|_L, /* 240-243 */ +_W|_L, _W|_L, _W|_L, _W|_P, /* 244-247 */ +_W|_L, _W|_L, _W|_L, _W|_L, /* 248-251 */ +_W|_L, _W|_L, _W|_L, _W|_L}; /* 252-255 */ + +static inline int match_token(struct ts_fsm_token *t, u8 d) +{ + if (t->type) + return (token_lookup_tbl[d] & t->type) != 0; + else + return t->value == d; +} + +static unsigned int fsm_find(struct ts_config *conf, struct ts_state *state) +{ + struct ts_fsm *fsm = ts_config_priv(conf); + struct ts_fsm_token *cur = NULL, *next; + unsigned int match_start, block_idx = 0, tok_idx; + unsigned block_len = 0, strict, consumed = state->offset; + const u8 *data; + +#define GET_NEXT_BLOCK() \ +({ consumed += block_idx; \ + block_idx = 0; \ + block_len = conf->get_next_block(consumed, &data, conf, state); }) + +#define TOKEN_MISMATCH() \ + do { \ + if (strict) \ + goto no_match; \ + block_idx++; \ + goto startover; \ + } while(0) + +#define end_of_data() unlikely(block_idx >= block_len && !GET_NEXT_BLOCK()) + + if (end_of_data()) + goto no_match; + + strict = fsm->tokens[0].recur != TS_FSM_HEAD_IGNORE; + +startover: + match_start = consumed + block_idx; + + for (tok_idx = 0; tok_idx < fsm->ntokens; tok_idx++) { + cur = &fsm->tokens[tok_idx]; + + if (likely(tok_idx < (fsm->ntokens - 1))) + next = &fsm->tokens[tok_idx + 1]; + else + next = NULL; + + switch (cur->recur) { + case TS_FSM_SINGLE: + if (end_of_data()) + goto no_match; + + if (!match_token(cur, data[block_idx])) + TOKEN_MISMATCH(); + break; + + case TS_FSM_PERHAPS: + if (end_of_data() || + !match_token(cur, data[block_idx])) + continue; + break; + + case TS_FSM_MULTI: + if (end_of_data()) + goto no_match; + + if (!match_token(cur, data[block_idx])) + TOKEN_MISMATCH(); + + block_idx++; + /* fall through */ + + case TS_FSM_ANY: + if (next == NULL) + goto found_match; + + if (end_of_data()) + continue; + + while (!match_token(next, data[block_idx])) { + if (!match_token(cur, data[block_idx])) + TOKEN_MISMATCH(); + block_idx++; + if (end_of_data()) + goto no_match; + } + continue; + + /* + * Optimization: Prefer small local loop over jumping + * back and forth until garbage at head is munched. + */ + case TS_FSM_HEAD_IGNORE: + if (end_of_data()) + continue; + + while (!match_token(next, data[block_idx])) { + /* + * Special case, don't start over upon + * a mismatch, give the user the + * chance to specify the type of data + * allowed to be ignored. + */ + if (!match_token(cur, data[block_idx])) + goto no_match; + + block_idx++; + if (end_of_data()) + goto no_match; + } + + match_start = consumed + block_idx; + continue; + } + + block_idx++; + } + + if (end_of_data()) + goto found_match; + +no_match: + return UINT_MAX; + +found_match: + state->offset = consumed + block_idx; + return match_start; +} + +static struct ts_config *fsm_init(const void *pattern, unsigned int len, + int gfp_mask) +{ + int i, err = -EINVAL; + struct ts_config *conf; + struct ts_fsm *fsm; + struct ts_fsm_token *tokens = (struct ts_fsm_token *) pattern; + unsigned int ntokens = len / sizeof(*tokens); + size_t priv_size = sizeof(*fsm) + len; + + if (len % sizeof(struct ts_fsm_token) || ntokens < 1) + goto errout; + + for (i = 0; i < ntokens; i++) { + struct ts_fsm_token *t = &tokens[i]; + + if (t->type > TS_FSM_TYPE_MAX || t->recur > TS_FSM_RECUR_MAX) + goto errout; + + if (t->recur == TS_FSM_HEAD_IGNORE && + (i != 0 || i == (ntokens - 1))) + goto errout; + } + + conf = alloc_ts_config(priv_size, gfp_mask); + if (IS_ERR(conf)) + return conf; + + fsm = ts_config_priv(conf); + fsm->ntokens = ntokens; + memcpy(fsm->tokens, pattern, len); + + for (i = 0; i < fsm->ntokens; i++) { + struct ts_fsm_token *t = &fsm->tokens[i]; + t->type = token_map[t->type]; + } + + return conf; + +errout: + return ERR_PTR(err); +} + +static void *fsm_get_pattern(struct ts_config *conf) +{ + struct ts_fsm *fsm = ts_config_priv(conf); + return fsm->tokens; +} + +static unsigned int fsm_get_pattern_len(struct ts_config *conf) +{ + struct ts_fsm *fsm = ts_config_priv(conf); + return fsm->ntokens * sizeof(struct ts_fsm_token); +} + +static struct ts_ops fsm_ops = { + .name = "fsm", + .find = fsm_find, + .init = fsm_init, + .get_pattern = fsm_get_pattern, + .get_pattern_len = fsm_get_pattern_len, + .owner = THIS_MODULE, + .list = LIST_HEAD_INIT(fsm_ops.list) +}; + +static int __init init_fsm(void) +{ + return textsearch_register(&fsm_ops); +} + +static void __exit exit_fsm(void) +{ + textsearch_unregister(&fsm_ops); +} + +MODULE_LICENSE("GPL"); + +module_init(init_fsm); +module_exit(exit_fsm); diff --git a/lib/ts_kmp.c b/lib/ts_kmp.c new file mode 100644 index 000000000000..73266b975585 --- /dev/null +++ b/lib/ts_kmp.c @@ -0,0 +1,145 @@ +/* + * lib/ts_kmp.c Knuth-Morris-Pratt text search implementation + * + * 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: Thomas Graf + * + * ========================================================================== + * + * Implements a linear-time string-matching algorithm due to Knuth, + * Morris, and Pratt [1]. Their algorithm avoids the explicit + * computation of the transition function DELTA altogether. Its + * matching time is O(n), for n being length(text), using just an + * auxiliary function PI[1..m], for m being length(pattern), + * precomputed from the pattern in time O(m). The array PI allows + * the transition function DELTA to be computed efficiently + * "on the fly" as needed. Roughly speaking, for any state + * "q" = 0,1,...,m and any character "a" in SIGMA, the value + * PI["q"] contains the information that is independent of "a" and + * is needed to compute DELTA("q", "a") [2]. Since the array PI + * has only m entries, whereas DELTA has O(m|SIGMA|) entries, we + * save a factor of |SIGMA| in the preprocessing time by computing + * PI rather than DELTA. + * + * [1] Cormen, Leiserson, Rivest, Stein + * Introdcution to Algorithms, 2nd Edition, MIT Press + * [2] See finite automation theory + */ + +#include +#include +#include +#include +#include + +struct ts_kmp +{ + u8 * pattern; + unsigned int pattern_len; + unsigned int prefix_tbl[0]; +}; + +static unsigned int kmp_find(struct ts_config *conf, struct ts_state *state) +{ + struct ts_kmp *kmp = ts_config_priv(conf); + unsigned int i, q = 0, text_len, consumed = state->offset; + const u8 *text; + + for (;;) { + text_len = conf->get_next_block(consumed, &text, conf, state); + + if (unlikely(text_len == 0)) + break; + + for (i = 0; i < text_len; i++) { + while (q > 0 && kmp->pattern[q] != text[i]) + q = kmp->prefix_tbl[q - 1]; + if (kmp->pattern[q] == text[i]) + q++; + if (unlikely(q == kmp->pattern_len)) { + state->offset = consumed + i + 1; + return state->offset - kmp->pattern_len; + } + } + + consumed += text_len; + } + + return UINT_MAX; +} + +static inline void compute_prefix_tbl(const u8 *pattern, unsigned int len, + unsigned int *prefix_tbl) +{ + unsigned int k, q; + + for (k = 0, q = 1; q < len; q++) { + while (k > 0 && pattern[k] != pattern[q]) + k = prefix_tbl[k-1]; + if (pattern[k] == pattern[q]) + k++; + prefix_tbl[q] = k; + } +} + +static struct ts_config *kmp_init(const void *pattern, unsigned int len, + int gfp_mask) +{ + struct ts_config *conf; + struct ts_kmp *kmp; + unsigned int prefix_tbl_len = len * sizeof(unsigned int); + size_t priv_size = sizeof(*kmp) + len + prefix_tbl_len; + + conf = alloc_ts_config(priv_size, gfp_mask); + if (IS_ERR(conf)) + return conf; + + kmp = ts_config_priv(conf); + kmp->pattern_len = len; + compute_prefix_tbl(pattern, len, kmp->prefix_tbl); + kmp->pattern = (u8 *) kmp->prefix_tbl + prefix_tbl_len; + memcpy(kmp->pattern, pattern, len); + + return conf; +} + +static void *kmp_get_pattern(struct ts_config *conf) +{ + struct ts_kmp *kmp = ts_config_priv(conf); + return kmp->pattern; +} + +static unsigned int kmp_get_pattern_len(struct ts_config *conf) +{ + struct ts_kmp *kmp = ts_config_priv(conf); + return kmp->pattern_len; +} + +static struct ts_ops kmp_ops = { + .name = "kmp", + .find = kmp_find, + .init = kmp_init, + .get_pattern = kmp_get_pattern, + .get_pattern_len = kmp_get_pattern_len, + .owner = THIS_MODULE, + .list = LIST_HEAD_INIT(kmp_ops.list) +}; + +static int __init init_kmp(void) +{ + return textsearch_register(&kmp_ops); +} + +static void __exit exit_kmp(void) +{ + textsearch_unregister(&kmp_ops); +} + +MODULE_LICENSE("GPL"); + +module_init(init_kmp); +module_exit(exit_kmp); diff --git a/mm/Kconfig b/mm/Kconfig new file mode 100644 index 000000000000..cd379936cac6 --- /dev/null +++ b/mm/Kconfig @@ -0,0 +1,91 @@ +config SELECT_MEMORY_MODEL + def_bool y + depends on EXPERIMENTAL || ARCH_SELECT_MEMORY_MODEL + +choice + prompt "Memory model" + depends on SELECT_MEMORY_MODEL + default DISCONTIGMEM_MANUAL if ARCH_DISCONTIGMEM_DEFAULT + default SPARSEMEM_MANUAL if ARCH_SPARSEMEM_DEFAULT + default FLATMEM_MANUAL + +config FLATMEM_MANUAL + bool "Flat Memory" + depends on !ARCH_DISCONTIGMEM_ENABLE || ARCH_FLATMEM_ENABLE + help + This option allows you to change some of the ways that + Linux manages its memory internally. Most users will + only have one option here: FLATMEM. This is normal + and a correct option. + + Some users of more advanced features like NUMA and + memory hotplug may have different options here. + DISCONTIGMEM is an more mature, better tested system, + but is incompatible with memory hotplug and may suffer + decreased performance over SPARSEMEM. If unsure between + "Sparse Memory" and "Discontiguous Memory", choose + "Discontiguous Memory". + + If unsure, choose this option (Flat Memory) over any other. + +config DISCONTIGMEM_MANUAL + bool "Discontigious Memory" + depends on ARCH_DISCONTIGMEM_ENABLE + help + This option provides enhanced support for discontiguous + memory systems, over FLATMEM. These systems have holes + in their physical address spaces, and this option provides + more efficient handling of these holes. However, the vast + majority of hardware has quite flat address spaces, and + can have degraded performance from extra overhead that + this option imposes. + + Many NUMA configurations will have this as the only option. + + If unsure, choose "Flat Memory" over this option. + +config SPARSEMEM_MANUAL + bool "Sparse Memory" + depends on ARCH_SPARSEMEM_ENABLE + help + This will be the only option for some systems, including + memory hotplug systems. This is normal. + + For many other systems, this will be an alternative to + "Discontigious Memory". This option provides some potential + performance benefits, along with decreased code complexity, + but it is newer, and more experimental. + + If unsure, choose "Discontiguous Memory" or "Flat Memory" + over this option. + +endchoice + +config DISCONTIGMEM + def_bool y + depends on (!SELECT_MEMORY_MODEL && ARCH_DISCONTIGMEM_ENABLE) || DISCONTIGMEM_MANUAL + +config SPARSEMEM + def_bool y + depends on SPARSEMEM_MANUAL + +config FLATMEM + def_bool y + depends on (!DISCONTIGMEM && !SPARSEMEM) || FLATMEM_MANUAL + +config FLAT_NODE_MEM_MAP + def_bool y + depends on !SPARSEMEM + +# +# Both the NUMA code and DISCONTIGMEM use arrays of pg_data_t's +# to represent different areas of memory. This variable allows +# those dependencies to exist individually. +# +config NEED_MULTIPLE_NODES + def_bool y + depends on DISCONTIGMEM || NUMA + +config HAVE_MEMORY_PRESENT + def_bool y + depends on ARCH_HAVE_MEMORY_PRESENT || SPARSEMEM diff --git a/mm/Makefile b/mm/Makefile index 097408064f6a..4cd69e3ce421 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -15,6 +15,8 @@ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o obj-$(CONFIG_NUMA) += mempolicy.o +obj-$(CONFIG_SPARSEMEM) += sparse.o obj-$(CONFIG_SHMEM) += shmem.o obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o +obj-$(CONFIG_FS_XIP) += filemap_xip.o diff --git a/mm/bootmem.c b/mm/bootmem.c index 260e703850d8..f82f7aebbee3 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -256,6 +256,7 @@ found: static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat) { struct page *page; + unsigned long pfn; bootmem_data_t *bdata = pgdat->bdata; unsigned long i, count, total = 0; unsigned long idx; @@ -266,7 +267,7 @@ static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat) count = 0; /* first extant page of the node */ - page = virt_to_page(phys_to_virt(bdata->node_boot_start)); + pfn = bdata->node_boot_start >> PAGE_SHIFT; idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT); map = bdata->node_bootmem_map; /* Check physaddr is O(LOG2(BITS_PER_LONG)) page aligned */ @@ -275,9 +276,11 @@ static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat) gofast = 1; for (i = 0; i < idx; ) { unsigned long v = ~map[i / BITS_PER_LONG]; + if (gofast && v == ~0UL) { int j, order; + page = pfn_to_page(pfn); count += BITS_PER_LONG; __ClearPageReserved(page); order = ffs(BITS_PER_LONG) - 1; @@ -292,6 +295,8 @@ static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat) page += BITS_PER_LONG; } else if (v) { unsigned long m; + + page = pfn_to_page(pfn); for (m = 1; m && i < idx; m<<=1, page++, i++) { if (v & m) { count++; @@ -302,8 +307,8 @@ static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat) } } else { i+=BITS_PER_LONG; - page += BITS_PER_LONG; } + pfn += BITS_PER_LONG; } total += count; diff --git a/mm/fadvise.c b/mm/fadvise.c index 57264d74b8bf..5f19e87bc5af 100644 --- a/mm/fadvise.c +++ b/mm/fadvise.c @@ -43,6 +43,10 @@ asmlinkage long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice) goto out; } + if (mapping->a_ops->get_xip_page) + /* no bad return value, but ignore advice */ + goto out; + /* Careful about overflows. Len == 0 means "as much as possible" */ endbyte = offset + len; if (!len || endbyte < len) diff --git a/mm/filemap.c b/mm/filemap.c index 4a2fee2cb62b..7332194d7afd 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -28,6 +28,7 @@ #include #include #include +#include "filemap.h" /* * FIXME: remove all knowledge of the buffer layer from the core VM */ @@ -1714,32 +1715,7 @@ int remove_suid(struct dentry *dentry) } EXPORT_SYMBOL(remove_suid); -/* - * Copy as much as we can into the page and return the number of bytes which - * were sucessfully copied. If a fault is encountered then clear the page - * out to (offset+bytes) and return the number of bytes which were copied. - */ -static inline size_t -filemap_copy_from_user(struct page *page, unsigned long offset, - const char __user *buf, unsigned bytes) -{ - char *kaddr; - int left; - - kaddr = kmap_atomic(page, KM_USER0); - left = __copy_from_user_inatomic(kaddr + offset, buf, bytes); - kunmap_atomic(kaddr, KM_USER0); - - if (left != 0) { - /* Do it the slow way */ - kaddr = kmap(page); - left = __copy_from_user(kaddr + offset, buf, bytes); - kunmap(page); - } - return bytes - left; -} - -static size_t +size_t __filemap_copy_from_user_iovec(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { @@ -1766,52 +1742,6 @@ __filemap_copy_from_user_iovec(char *vaddr, return copied - left; } -/* - * This has the same sideeffects and return value as filemap_copy_from_user(). - * The difference is that on a fault we need to memset the remainder of the - * page (out to offset+bytes), to emulate filemap_copy_from_user()'s - * single-segment behaviour. - */ -static inline size_t -filemap_copy_from_user_iovec(struct page *page, unsigned long offset, - const struct iovec *iov, size_t base, size_t bytes) -{ - char *kaddr; - size_t copied; - - kaddr = kmap_atomic(page, KM_USER0); - copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, - base, bytes); - kunmap_atomic(kaddr, KM_USER0); - if (copied != bytes) { - kaddr = kmap(page); - copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, - base, bytes); - kunmap(page); - } - return copied; -} - -static inline void -filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) -{ - const struct iovec *iov = *iovp; - size_t base = *basep; - - while (bytes) { - int copy = min(bytes, iov->iov_len - base); - - bytes -= copy; - base += copy; - if (iov->iov_len == base) { - iov++; - base = 0; - } - } - *iovp = iov; - *basep = base; -} - /* * Performs necessary checks before doing a write * @@ -1827,12 +1757,6 @@ inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, i if (unlikely(*pos < 0)) return -EINVAL; - if (unlikely(file->f_error)) { - int err = file->f_error; - file->f_error = 0; - return err; - } - if (!isblk) { /* FIXME: this is for backwards compatibility with 2.4 */ if (file->f_flags & O_APPEND) diff --git a/mm/filemap.h b/mm/filemap.h new file mode 100644 index 000000000000..13793ba0ce17 --- /dev/null +++ b/mm/filemap.h @@ -0,0 +1,94 @@ +/* + * linux/mm/filemap.h + * + * Copyright (C) 1994-1999 Linus Torvalds + */ + +#ifndef __FILEMAP_H +#define __FILEMAP_H + +#include +#include +#include +#include +#include +#include +#include + +size_t +__filemap_copy_from_user_iovec(char *vaddr, + const struct iovec *iov, + size_t base, + size_t bytes); + +/* + * Copy as much as we can into the page and return the number of bytes which + * were sucessfully copied. If a fault is encountered then clear the page + * out to (offset+bytes) and return the number of bytes which were copied. + */ +static inline size_t +filemap_copy_from_user(struct page *page, unsigned long offset, + const char __user *buf, unsigned bytes) +{ + char *kaddr; + int left; + + kaddr = kmap_atomic(page, KM_USER0); + left = __copy_from_user_inatomic(kaddr + offset, buf, bytes); + kunmap_atomic(kaddr, KM_USER0); + + if (left != 0) { + /* Do it the slow way */ + kaddr = kmap(page); + left = __copy_from_user(kaddr + offset, buf, bytes); + kunmap(page); + } + return bytes - left; +} + +/* + * This has the same sideeffects and return value as filemap_copy_from_user(). + * The difference is that on a fault we need to memset the remainder of the + * page (out to offset+bytes), to emulate filemap_copy_from_user()'s + * single-segment behaviour. + */ +static inline size_t +filemap_copy_from_user_iovec(struct page *page, unsigned long offset, + const struct iovec *iov, size_t base, size_t bytes) +{ + char *kaddr; + size_t copied; + + kaddr = kmap_atomic(page, KM_USER0); + copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, + base, bytes); + kunmap_atomic(kaddr, KM_USER0); + if (copied != bytes) { + kaddr = kmap(page); + copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, + base, bytes); + kunmap(page); + } + return copied; +} + +static inline void +filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) +{ + const struct iovec *iov = *iovp; + size_t base = *basep; + + while (bytes) { + int copy = min(bytes, iov->iov_len - base); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + base = 0; + } + } + *iovp = iov; + *basep = base; +} +#endif diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c new file mode 100644 index 000000000000..3b6e384b98a6 --- /dev/null +++ b/mm/filemap_xip.c @@ -0,0 +1,447 @@ +/* + * linux/mm/filemap_xip.c + * + * Copyright (C) 2005 IBM Corporation + * Author: Carsten Otte + * + * derived from linux/mm/filemap.c - Copyright (C) Linus Torvalds + * + */ + +#include +#include +#include +#include +#include +#include +#include "filemap.h" + +/* + * This is a file read routine for execute in place files, and uses + * the mapping->a_ops->get_xip_page() function for the actual low-level + * stuff. + * + * Note the struct file* is not used at all. It may be NULL. + */ +static void +do_xip_mapping_read(struct address_space *mapping, + struct file_ra_state *_ra, + struct file *filp, + loff_t *ppos, + read_descriptor_t *desc, + read_actor_t actor) +{ + struct inode *inode = mapping->host; + unsigned long index, end_index, offset; + loff_t isize; + + BUG_ON(!mapping->a_ops->get_xip_page); + + index = *ppos >> PAGE_CACHE_SHIFT; + offset = *ppos & ~PAGE_CACHE_MASK; + + isize = i_size_read(inode); + if (!isize) + goto out; + + end_index = (isize - 1) >> PAGE_CACHE_SHIFT; + for (;;) { + struct page *page; + unsigned long nr, ret; + + /* nr is the maximum number of bytes to copy from this page */ + nr = PAGE_CACHE_SIZE; + if (index >= end_index) { + if (index > end_index) + goto out; + nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; + if (nr <= offset) { + goto out; + } + } + nr = nr - offset; + + page = mapping->a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 0); + if (!page) + goto no_xip_page; + if (unlikely(IS_ERR(page))) { + if (PTR_ERR(page) == -ENODATA) { + /* sparse */ + page = virt_to_page(empty_zero_page); + } else { + desc->error = PTR_ERR(page); + goto out; + } + } else + BUG_ON(!PageUptodate(page)); + + /* If users can be writing to this page using arbitrary + * virtual addresses, take care about potential aliasing + * before reading the page on the kernel side. + */ + if (mapping_writably_mapped(mapping)) + flush_dcache_page(page); + + /* + * Ok, we have the page, and it's up-to-date, so + * now we can copy it to user space... + * + * The actor routine returns how many bytes were actually used.. + * NOTE! This may not be the same as how much of a user buffer + * we filled up (we may be padding etc), so we can only update + * "pos" here (the actor routine has to update the user buffer + * pointers and the remaining count). + */ + ret = actor(desc, page, offset, nr); + offset += ret; + index += offset >> PAGE_CACHE_SHIFT; + offset &= ~PAGE_CACHE_MASK; + + if (ret == nr && desc->count) + continue; + goto out; + +no_xip_page: + /* Did not get the page. Report it */ + desc->error = -EIO; + goto out; + } + +out: + *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset; + if (filp) + file_accessed(filp); +} + +ssize_t +xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) +{ + read_descriptor_t desc; + + if (!access_ok(VERIFY_WRITE, buf, len)) + return -EFAULT; + + desc.written = 0; + desc.arg.buf = buf; + desc.count = len; + desc.error = 0; + + do_xip_mapping_read(filp->f_mapping, &filp->f_ra, filp, + ppos, &desc, file_read_actor); + + if (desc.written) + return desc.written; + else + return desc.error; +} +EXPORT_SYMBOL_GPL(xip_file_read); + +ssize_t +xip_file_sendfile(struct file *in_file, loff_t *ppos, + size_t count, read_actor_t actor, void *target) +{ + read_descriptor_t desc; + + if (!count) + return 0; + + desc.written = 0; + desc.count = count; + desc.arg.data = target; + desc.error = 0; + + do_xip_mapping_read(in_file->f_mapping, &in_file->f_ra, in_file, + ppos, &desc, actor); + if (desc.written) + return desc.written; + return desc.error; +} +EXPORT_SYMBOL_GPL(xip_file_sendfile); + +/* + * __xip_unmap is invoked from xip_unmap and + * xip_write + * + * This function walks all vmas of the address_space and unmaps the + * empty_zero_page when found at pgoff. Should it go in rmap.c? + */ +static void +__xip_unmap (struct address_space * mapping, + unsigned long pgoff) +{ + struct vm_area_struct *vma; + struct mm_struct *mm; + struct prio_tree_iter iter; + unsigned long address; + pte_t *pte; + pte_t pteval; + + spin_lock(&mapping->i_mmap_lock); + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + mm = vma->vm_mm; + address = vma->vm_start + + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); + BUG_ON(address < vma->vm_start || address >= vma->vm_end); + /* + * We need the page_table_lock to protect us from page faults, + * munmap, fork, etc... + */ + pte = page_check_address(virt_to_page(empty_zero_page), mm, + address); + if (!IS_ERR(pte)) { + /* Nuke the page table entry. */ + flush_cache_page(vma, address, pte_pfn(pte)); + pteval = ptep_clear_flush(vma, address, pte); + BUG_ON(pte_dirty(pteval)); + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + } + } + spin_unlock(&mapping->i_mmap_lock); +} + +/* + * xip_nopage() is invoked via the vma operations vector for a + * mapped memory region to read in file data during a page fault. + * + * This function is derived from filemap_nopage, but used for execute in place + */ +static struct page * +xip_file_nopage(struct vm_area_struct * area, + unsigned long address, + int *type) +{ + struct file *file = area->vm_file; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + struct page *page; + unsigned long size, pgoff, endoff; + + pgoff = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + + area->vm_pgoff; + endoff = ((area->vm_end - area->vm_start) >> PAGE_CACHE_SHIFT) + + area->vm_pgoff; + + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (pgoff >= size) { + return NULL; + } + + page = mapping->a_ops->get_xip_page(mapping, pgoff*(PAGE_SIZE/512), 0); + if (!IS_ERR(page)) { + BUG_ON(!PageUptodate(page)); + return page; + } + if (PTR_ERR(page) != -ENODATA) + return NULL; + + /* sparse block */ + if ((area->vm_flags & (VM_WRITE | VM_MAYWRITE)) && + (area->vm_flags & (VM_SHARED| VM_MAYSHARE)) && + (!(mapping->host->i_sb->s_flags & MS_RDONLY))) { + /* maybe shared writable, allocate new block */ + page = mapping->a_ops->get_xip_page (mapping, + pgoff*(PAGE_SIZE/512), 1); + if (IS_ERR(page)) + return NULL; + BUG_ON(!PageUptodate(page)); + /* unmap page at pgoff from all other vmas */ + __xip_unmap(mapping, pgoff); + } else { + /* not shared and writable, use empty_zero_page */ + page = virt_to_page(empty_zero_page); + } + + return page; +} + +static struct vm_operations_struct xip_file_vm_ops = { + .nopage = xip_file_nopage, +}; + +int xip_file_mmap(struct file * file, struct vm_area_struct * vma) +{ + BUG_ON(!file->f_mapping->a_ops->get_xip_page); + + file_accessed(file); + vma->vm_ops = &xip_file_vm_ops; + return 0; +} +EXPORT_SYMBOL_GPL(xip_file_mmap); + +static ssize_t +__xip_file_write(struct file *filp, const char __user *buf, + size_t count, loff_t pos, loff_t *ppos) +{ + struct address_space * mapping = filp->f_mapping; + struct address_space_operations *a_ops = mapping->a_ops; + struct inode *inode = mapping->host; + long status = 0; + struct page *page; + size_t bytes; + ssize_t written = 0; + + BUG_ON(!mapping->a_ops->get_xip_page); + + do { + unsigned long index; + unsigned long offset; + size_t copied; + + offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */ + index = pos >> PAGE_CACHE_SHIFT; + bytes = PAGE_CACHE_SIZE - offset; + if (bytes > count) + bytes = count; + + /* + * Bring in the user page that we will copy from _first_. + * Otherwise there's a nasty deadlock on copying from the + * same page as we're writing to, without it being marked + * up-to-date. + */ + fault_in_pages_readable(buf, bytes); + + page = a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 0); + if (IS_ERR(page) && (PTR_ERR(page) == -ENODATA)) { + /* we allocate a new page unmap it */ + page = a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 1); + if (!IS_ERR(page)) + /* unmap page at pgoff from all other vmas */ + __xip_unmap(mapping, index); + } + + if (IS_ERR(page)) { + status = PTR_ERR(page); + break; + } + + BUG_ON(!PageUptodate(page)); + + copied = filemap_copy_from_user(page, offset, buf, bytes); + flush_dcache_page(page); + if (likely(copied > 0)) { + status = copied; + + if (status >= 0) { + written += status; + count -= status; + pos += status; + buf += status; + } + } + if (unlikely(copied != bytes)) + if (status >= 0) + status = -EFAULT; + if (status < 0) + break; + } while (count); + *ppos = pos; + /* + * No need to use i_size_read() here, the i_size + * cannot change under us because we hold i_sem. + */ + if (pos > inode->i_size) { + i_size_write(inode, pos); + mark_inode_dirty(inode); + } + + return written ? written : status; +} + +ssize_t +xip_file_write(struct file *filp, const char __user *buf, size_t len, + loff_t *ppos) +{ + struct address_space *mapping = filp->f_mapping; + struct inode *inode = mapping->host; + size_t count; + loff_t pos; + ssize_t ret; + + down(&inode->i_sem); + + if (!access_ok(VERIFY_READ, buf, len)) { + ret=-EFAULT; + goto out_up; + } + + pos = *ppos; + count = len; + + vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); + + /* We can write back this queue in page reclaim */ + current->backing_dev_info = mapping->backing_dev_info; + + ret = generic_write_checks(filp, &pos, &count, S_ISBLK(inode->i_mode)); + if (ret) + goto out_backing; + if (count == 0) + goto out_backing; + + ret = remove_suid(filp->f_dentry); + if (ret) + goto out_backing; + + inode_update_time(inode, 1); + + ret = __xip_file_write (filp, buf, count, pos, ppos); + + out_backing: + current->backing_dev_info = NULL; + out_up: + up(&inode->i_sem); + return ret; +} +EXPORT_SYMBOL_GPL(xip_file_write); + +/* + * truncate a page used for execute in place + * functionality is analog to block_truncate_page but does use get_xip_page + * to get the page instead of page cache + */ +int +xip_truncate_page(struct address_space *mapping, loff_t from) +{ + pgoff_t index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_CACHE_SIZE-1); + unsigned blocksize; + unsigned length; + struct page *page; + void *kaddr; + + BUG_ON(!mapping->a_ops->get_xip_page); + + blocksize = 1 << mapping->host->i_blkbits; + length = offset & (blocksize - 1); + + /* Block boundary? Nothing to do */ + if (!length) + return 0; + + length = blocksize - length; + + page = mapping->a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 0); + if (!page) + return -ENOMEM; + if (unlikely(IS_ERR(page))) { + if (PTR_ERR(page) == -ENODATA) + /* Hole? No need to truncate */ + return 0; + else + return PTR_ERR(page); + } else + BUG_ON(!PageUptodate(page)); + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + offset, 0, length); + kunmap_atomic(kaddr, KM_USER0); + + flush_dcache_page(page); + return 0; +} +EXPORT_SYMBOL_GPL(xip_truncate_page); diff --git a/mm/madvise.c b/mm/madvise.c index e3108054733c..73180a22877e 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -65,7 +65,6 @@ static long madvise_behavior(struct vm_area_struct * vma, /* * vm_flags is protected by the mmap_sem held in write mode. */ - VM_ClearReadHint(vma); vma->vm_flags = new_flags; out: @@ -87,6 +86,11 @@ static long madvise_willneed(struct vm_area_struct * vma, if (!file) return -EBADF; + if (file->f_mapping->a_ops->get_xip_page) { + /* no bad return value, but ignore advice */ + return 0; + } + *prev = vma; start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; if (end > vma->vm_end) diff --git a/mm/memory.c b/mm/memory.c index da91b7bf9986..c256175742ac 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -58,7 +58,7 @@ #include #include -#ifndef CONFIG_DISCONTIGMEM +#ifndef CONFIG_NEED_MULTIPLE_NODES /* use the per-pgdat data instead for discontigmem - mbligh */ unsigned long max_mapnr; struct page *mem_map; @@ -1458,7 +1458,7 @@ restart: * unmap_mapping_range - unmap the portion of all mmaps * in the specified address_space corresponding to the specified * page range in the underlying file. - * @address_space: the address space containing mmaps to be unmapped. + * @mapping: the address space containing mmaps to be unmapped. * @holebegin: byte in first page to unmap, relative to the start of * the underlying file. This will be rounded down to a PAGE_SIZE * boundary. Note that this is different from vmtruncate(), which diff --git a/mm/mempool.c b/mm/mempool.c index c9f3d4620428..9a72f7d918fa 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -51,16 +51,23 @@ static void free_pool(mempool_t *pool) * functions might sleep - as long as the mempool_alloc function is not called * from IRQ contexts. */ -mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn, +mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data) { - mempool_t *pool; + return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,-1); +} +EXPORT_SYMBOL(mempool_create); - pool = kmalloc(sizeof(*pool), GFP_KERNEL); +mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, + mempool_free_t *free_fn, void *pool_data, int node_id) +{ + mempool_t *pool; + pool = kmalloc_node(sizeof(*pool), GFP_KERNEL, node_id); if (!pool) return NULL; memset(pool, 0, sizeof(*pool)); - pool->elements = kmalloc(min_nr * sizeof(void *), GFP_KERNEL); + pool->elements = kmalloc_node(min_nr * sizeof(void *), + GFP_KERNEL, node_id); if (!pool->elements) { kfree(pool); return NULL; @@ -87,7 +94,7 @@ mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn, } return pool; } -EXPORT_SYMBOL(mempool_create); +EXPORT_SYMBOL(mempool_create_node); /** * mempool_resize - resize an existing memory pool @@ -197,7 +204,7 @@ void * mempool_alloc(mempool_t *pool, unsigned int __nocast gfp_mask) { void *element; unsigned long flags; - DEFINE_WAIT(wait); + wait_queue_t wait; int gfp_temp; might_sleep_if(gfp_mask & __GFP_WAIT); @@ -228,6 +235,7 @@ repeat_alloc: /* Now start performing page reclaim */ gfp_temp = gfp_mask; + init_wait(&wait); prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE); smp_mb(); if (!pool->curr_nr) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 559336de9687..7ee675ad101e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -68,7 +68,7 @@ EXPORT_SYMBOL(nr_swap_pages); * Used by page_zone() to look up the address of the struct zone whose * id is encoded in the upper bits of page->flags */ -struct zone *zone_table[1 << (ZONES_SHIFT + NODES_SHIFT)]; +struct zone *zone_table[1 << ZONETABLE_SHIFT]; EXPORT_SYMBOL(zone_table); static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; @@ -1649,11 +1649,17 @@ static void __init calculate_zone_totalpages(struct pglist_data *pgdat, void __init memmap_init_zone(unsigned long size, int nid, unsigned long zone, unsigned long start_pfn) { - struct page *start = pfn_to_page(start_pfn); struct page *page; + unsigned long end_pfn = start_pfn + size; + unsigned long pfn; - for (page = start; page < (start + size); page++) { - set_page_zone(page, NODEZONE(nid, zone)); + for (pfn = start_pfn; pfn < end_pfn; pfn++, page++) { + if (!early_pfn_valid(pfn)) + continue; + if (!early_pfn_in_nid(pfn, nid)) + continue; + page = pfn_to_page(pfn); + set_page_links(page, zone, nid, pfn); set_page_count(page, 0); reset_page_mapcount(page); SetPageReserved(page); @@ -1677,6 +1683,20 @@ void zone_init_free_lists(struct pglist_data *pgdat, struct zone *zone, } } +#define ZONETABLE_INDEX(x, zone_nr) ((x << ZONES_SHIFT) | zone_nr) +void zonetable_add(struct zone *zone, int nid, int zid, unsigned long pfn, + unsigned long size) +{ + unsigned long snum = pfn_to_section_nr(pfn); + unsigned long end = pfn_to_section_nr(pfn + size); + + if (FLAGS_HAS_NODE) + zone_table[ZONETABLE_INDEX(nid, zid)] = zone; + else + for (; snum <= end; snum++) + zone_table[ZONETABLE_INDEX(snum, zid)] = zone; +} + #ifndef __HAVE_ARCH_MEMMAP_INIT #define memmap_init(size, nid, zone, start_pfn) \ memmap_init_zone((size), (nid), (zone), (start_pfn)) @@ -1861,7 +1881,6 @@ static void __init free_area_init_core(struct pglist_data *pgdat, unsigned long size, realsize; unsigned long batch; - zone_table[NODEZONE(nid, j)] = zone; realsize = size = zones_size[j]; if (zholes_size) realsize -= zholes_size[j]; @@ -1927,6 +1946,8 @@ static void __init free_area_init_core(struct pglist_data *pgdat, memmap_init(size, nid, j, zone_start_pfn); + zonetable_add(zone, nid, j, zone_start_pfn, size); + zone_start_pfn += size; zone_init_free_lists(pgdat, zone, zone->spanned_pages); @@ -1935,24 +1956,30 @@ static void __init free_area_init_core(struct pglist_data *pgdat, static void __init alloc_node_mem_map(struct pglist_data *pgdat) { - unsigned long size; - /* Skip empty nodes */ if (!pgdat->node_spanned_pages) return; +#ifdef CONFIG_FLAT_NODE_MEM_MAP /* ia64 gets its own node_mem_map, before this, without bootmem */ if (!pgdat->node_mem_map) { + unsigned long size; + struct page *map; + size = (pgdat->node_spanned_pages + 1) * sizeof(struct page); - pgdat->node_mem_map = alloc_bootmem_node(pgdat, size); + map = alloc_remap(pgdat->node_id, size); + if (!map) + map = alloc_bootmem_node(pgdat, size); + pgdat->node_mem_map = map; } -#ifndef CONFIG_DISCONTIGMEM +#ifdef CONFIG_FLATMEM /* * With no DISCONTIG, the global mem_map is just set as node 0's */ if (pgdat == NODE_DATA(0)) mem_map = NODE_DATA(0)->node_mem_map; #endif +#endif /* CONFIG_FLAT_NODE_MEM_MAP */ } void __init free_area_init_node(int nid, struct pglist_data *pgdat, @@ -1968,18 +1995,18 @@ void __init free_area_init_node(int nid, struct pglist_data *pgdat, free_area_init_core(pgdat, zones_size, zholes_size); } -#ifndef CONFIG_DISCONTIGMEM +#ifndef CONFIG_NEED_MULTIPLE_NODES static bootmem_data_t contig_bootmem_data; struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data }; EXPORT_SYMBOL(contig_page_data); +#endif void __init free_area_init(unsigned long *zones_size) { - free_area_init_node(0, &contig_page_data, zones_size, + free_area_init_node(0, NODE_DATA(0), zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL); } -#endif #ifdef CONFIG_PROC_FS diff --git a/mm/rmap.c b/mm/rmap.c index 89770bd25f31..08ac5c7fa91f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -247,8 +247,8 @@ unsigned long page_address_in_vma(struct page *page, struct vm_area_struct *vma) * * On success returns with mapped pte and locked mm->page_table_lock. */ -static pte_t *page_check_address(struct page *page, struct mm_struct *mm, - unsigned long address) +pte_t *page_check_address(struct page *page, struct mm_struct *mm, + unsigned long address) { pgd_t *pgd; pud_t *pud; diff --git a/mm/slab.c b/mm/slab.c index 93cbbbb39f42..122d031baab2 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -92,6 +92,7 @@ #include #include #include +#include #include #include @@ -3082,3 +3083,26 @@ unsigned int ksize(const void *objp) return size; } + + +/* + * kstrdup - allocate space for and copy an existing string + * + * @s: the string to duplicate + * @gfp: the GFP mask used in the kmalloc() call when allocating memory + */ +char *kstrdup(const char *s, int gfp) +{ + size_t len; + char *buf; + + if (!s) + return NULL; + + len = strlen(s) + 1; + buf = kmalloc(len, gfp); + if (buf) + memcpy(buf, s, len); + return buf; +} +EXPORT_SYMBOL(kstrdup); diff --git a/mm/sparse.c b/mm/sparse.c new file mode 100644 index 000000000000..b54e304df4a7 --- /dev/null +++ b/mm/sparse.c @@ -0,0 +1,137 @@ +/* + * sparse memory mappings. + */ +#include +#include +#include +#include +#include +#include + +/* + * Permanent SPARSEMEM data: + * + * 1) mem_section - memory sections, mem_map's for valid memory + */ +struct mem_section mem_section[NR_MEM_SECTIONS]; +EXPORT_SYMBOL(mem_section); + +/* Record a memory area against a node. */ +void memory_present(int nid, unsigned long start, unsigned long end) +{ + unsigned long pfn; + + start &= PAGE_SECTION_MASK; + for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) { + unsigned long section = pfn_to_section_nr(pfn); + if (!mem_section[section].section_mem_map) + mem_section[section].section_mem_map = SECTION_MARKED_PRESENT; + } +} + +/* + * Only used by the i386 NUMA architecures, but relatively + * generic code. + */ +unsigned long __init node_memmap_size_bytes(int nid, unsigned long start_pfn, + unsigned long end_pfn) +{ + unsigned long pfn; + unsigned long nr_pages = 0; + + for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { + if (nid != early_pfn_to_nid(pfn)) + continue; + + if (pfn_valid(pfn)) + nr_pages += PAGES_PER_SECTION; + } + + return nr_pages * sizeof(struct page); +} + +/* + * Subtle, we encode the real pfn into the mem_map such that + * the identity pfn - section_mem_map will return the actual + * physical page frame number. + */ +static unsigned long sparse_encode_mem_map(struct page *mem_map, unsigned long pnum) +{ + return (unsigned long)(mem_map - (section_nr_to_pfn(pnum))); +} + +/* + * We need this if we ever free the mem_maps. While not implemented yet, + * this function is included for parity with its sibling. + */ +static __attribute((unused)) +struct page *sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long pnum) +{ + return ((struct page *)coded_mem_map) + section_nr_to_pfn(pnum); +} + +static int sparse_init_one_section(struct mem_section *ms, + unsigned long pnum, struct page *mem_map) +{ + if (!valid_section(ms)) + return -EINVAL; + + ms->section_mem_map |= sparse_encode_mem_map(mem_map, pnum); + + return 1; +} + +static struct page *sparse_early_mem_map_alloc(unsigned long pnum) +{ + struct page *map; + int nid = early_pfn_to_nid(section_nr_to_pfn(pnum)); + + map = alloc_remap(nid, sizeof(struct page) * PAGES_PER_SECTION); + if (map) + return map; + + map = alloc_bootmem_node(NODE_DATA(nid), + sizeof(struct page) * PAGES_PER_SECTION); + if (map) + return map; + + printk(KERN_WARNING "%s: allocation failed\n", __FUNCTION__); + mem_section[pnum].section_mem_map = 0; + return NULL; +} + +/* + * Allocate the accumulated non-linear sections, allocate a mem_map + * for each and record the physical to section mapping. + */ +void sparse_init(void) +{ + unsigned long pnum; + struct page *map; + + for (pnum = 0; pnum < NR_MEM_SECTIONS; pnum++) { + if (!valid_section_nr(pnum)) + continue; + + map = sparse_early_mem_map_alloc(pnum); + if (map) + sparse_init_one_section(&mem_section[pnum], pnum, map); + } +} + +/* + * returns the number of sections whose mem_maps were properly + * set. If this is <=0, then that means that the passed-in + * map was not consumed and must be freed. + */ +int sparse_add_one_section(unsigned long start_pfn, int nr_pages, struct page *map) +{ + struct mem_section *ms = __pfn_to_section(start_pfn); + + if (ms->section_mem_map & SECTION_MARKED_PRESENT) + return -EEXIST; + + ms->section_mem_map |= SECTION_MARKED_PRESENT; + + return sparse_init_one_section(ms, pfn_to_section_nr(start_pfn), map); +} diff --git a/net/core/dev.c b/net/core/dev.c index ab935778ce81..7016e0c36b3d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -115,18 +115,6 @@ #endif /* CONFIG_NET_RADIO */ #include -/* This define, if set, will randomly drop a packet when congestion - * is more than moderate. It helps fairness in the multi-interface - * case when one of them is a hog, but it kills performance for the - * single interface case so it is off now by default. - */ -#undef RAND_LIE - -/* Setting this will sample the queue lengths and thus congestion - * via a timer instead of as each packet is received. - */ -#undef OFFLINE_SAMPLE - /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. @@ -159,11 +147,6 @@ static DEFINE_SPINLOCK(ptype_lock); static struct list_head ptype_base[16]; /* 16 way hashed list */ static struct list_head ptype_all; /* Taps */ -#ifdef OFFLINE_SAMPLE -static void sample_queue(unsigned long dummy); -static struct timer_list samp_timer = TIMER_INITIALIZER(sample_queue, 0, 0); -#endif - /* * The @dev_base list is protected by @dev_base_lock and the rtln * semaphore. @@ -215,7 +198,7 @@ static struct notifier_block *netdev_chain; * Device drivers call our routines to queue packets here. We empty the * queue in the local softnet handler. */ -DEFINE_PER_CPU(struct softnet_data, softnet_data) = { 0, }; +DEFINE_PER_CPU(struct softnet_data, softnet_data) = { NULL }; #ifdef CONFIG_SYSFS extern int netdev_sysfs_init(void); @@ -1363,71 +1346,13 @@ out: Receiver routines =======================================================================*/ -int netdev_max_backlog = 300; +int netdev_max_backlog = 1000; +int netdev_budget = 300; int weight_p = 64; /* old backlog weight */ -/* These numbers are selected based on intuition and some - * experimentatiom, if you have more scientific way of doing this - * please go ahead and fix things. - */ -int no_cong_thresh = 10; -int no_cong = 20; -int lo_cong = 100; -int mod_cong = 290; DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, }; -static void get_sample_stats(int cpu) -{ -#ifdef RAND_LIE - unsigned long rd; - int rq; -#endif - struct softnet_data *sd = &per_cpu(softnet_data, cpu); - int blog = sd->input_pkt_queue.qlen; - int avg_blog = sd->avg_blog; - - avg_blog = (avg_blog >> 1) + (blog >> 1); - - if (avg_blog > mod_cong) { - /* Above moderate congestion levels. */ - sd->cng_level = NET_RX_CN_HIGH; -#ifdef RAND_LIE - rd = net_random(); - rq = rd % netdev_max_backlog; - if (rq < avg_blog) /* unlucky bastard */ - sd->cng_level = NET_RX_DROP; -#endif - } else if (avg_blog > lo_cong) { - sd->cng_level = NET_RX_CN_MOD; -#ifdef RAND_LIE - rd = net_random(); - rq = rd % netdev_max_backlog; - if (rq < avg_blog) /* unlucky bastard */ - sd->cng_level = NET_RX_CN_HIGH; -#endif - } else if (avg_blog > no_cong) - sd->cng_level = NET_RX_CN_LOW; - else /* no congestion */ - sd->cng_level = NET_RX_SUCCESS; - - sd->avg_blog = avg_blog; -} - -#ifdef OFFLINE_SAMPLE -static void sample_queue(unsigned long dummy) -{ -/* 10 ms 0r 1ms -- i don't care -- JHS */ - int next_tick = 1; - int cpu = smp_processor_id(); - - get_sample_stats(cpu); - next_tick += jiffies; - mod_timer(&samp_timer, next_tick); -} -#endif - - /** * netif_rx - post buffer to the network code * @skb: buffer to post @@ -1448,7 +1373,6 @@ static void sample_queue(unsigned long dummy) int netif_rx(struct sk_buff *skb) { - int this_cpu; struct softnet_data *queue; unsigned long flags; @@ -1464,38 +1388,22 @@ int netif_rx(struct sk_buff *skb) * short when CPU is congested, but is still operating. */ local_irq_save(flags); - this_cpu = smp_processor_id(); queue = &__get_cpu_var(softnet_data); __get_cpu_var(netdev_rx_stat).total++; if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen) { - if (queue->throttle) - goto drop; - enqueue: dev_hold(skb->dev); __skb_queue_tail(&queue->input_pkt_queue, skb); -#ifndef OFFLINE_SAMPLE - get_sample_stats(this_cpu); -#endif local_irq_restore(flags); - return queue->cng_level; + return NET_RX_SUCCESS; } - if (queue->throttle) - queue->throttle = 0; - netif_rx_schedule(&queue->backlog_dev); goto enqueue; } - if (!queue->throttle) { - queue->throttle = 1; - __get_cpu_var(netdev_rx_stat).throttled++; - } - -drop: __get_cpu_var(netdev_rx_stat).dropped++; local_irq_restore(flags); @@ -1780,8 +1688,6 @@ job_done: smp_mb__before_clear_bit(); netif_poll_enable(backlog_dev); - if (queue->throttle) - queue->throttle = 0; local_irq_enable(); return 0; } @@ -1790,8 +1696,7 @@ static void net_rx_action(struct softirq_action *h) { struct softnet_data *queue = &__get_cpu_var(softnet_data); unsigned long start_time = jiffies; - int budget = netdev_max_backlog; - + int budget = netdev_budget; local_irq_disable(); @@ -2055,15 +1960,9 @@ static int softnet_seq_show(struct seq_file *seq, void *v) struct netif_rx_stats *s = v; seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", - s->total, s->dropped, s->time_squeeze, s->throttled, - s->fastroute_hit, s->fastroute_success, s->fastroute_defer, - s->fastroute_deferred_out, -#if 0 - s->fastroute_latency_reduction -#else - s->cpu_collision -#endif - ); + s->total, s->dropped, s->time_squeeze, 0, + 0, 0, 0, 0, /* was fastroute */ + s->cpu_collision ); return 0; } @@ -3305,9 +3204,6 @@ static int __init net_dev_init(void) queue = &per_cpu(softnet_data, i); skb_queue_head_init(&queue->input_pkt_queue); - queue->throttle = 0; - queue->cng_level = 0; - queue->avg_blog = 10; /* arbitrary non-zero */ queue->completion_queue = NULL; INIT_LIST_HEAD(&queue->poll_list); set_bit(__LINK_STATE_START, &queue->backlog_dev.state); @@ -3316,11 +3212,6 @@ static int __init net_dev_init(void) atomic_set(&queue->backlog_dev.refcnt, 1); } -#ifdef OFFLINE_SAMPLE - samp_timer.expires = jiffies + (10 * HZ); - add_timer(&samp_timer); -#endif - dev_boot_phase = 0; open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index f6bdcad47da6..851eb927ed97 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -32,6 +32,7 @@ #include #include #include +#include #define NEIGH_DEBUG 1 @@ -2592,7 +2593,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, t->neigh_vars[17].extra1 = dev; } - dev_name = net_sysctl_strdup(dev_name_source); + dev_name = kstrdup(dev_name_source, GFP_KERNEL); if (!dev_name) { err = -ENOBUFS; goto free; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6d68c03bc051..bb73b2190ec7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1500,6 +1500,159 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) skb_split_no_header(skb, skb1, len, pos); } +/** + * skb_prepare_seq_read - Prepare a sequential read of skb data + * @skb: the buffer to read + * @from: lower offset of data to be read + * @to: upper offset of data to be read + * @st: state variable + * + * Initializes the specified state variable. Must be called before + * invoking skb_seq_read() for the first time. + */ +void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, + unsigned int to, struct skb_seq_state *st) +{ + st->lower_offset = from; + st->upper_offset = to; + st->root_skb = st->cur_skb = skb; + st->frag_idx = st->stepped_offset = 0; + st->frag_data = NULL; +} + +/** + * skb_seq_read - Sequentially read skb data + * @consumed: number of bytes consumed by the caller so far + * @data: destination pointer for data to be returned + * @st: state variable + * + * Reads a block of skb data at &consumed relative to the + * lower offset specified to skb_prepare_seq_read(). Assigns + * the head of the data block to &data and returns the length + * of the block or 0 if the end of the skb data or the upper + * offset has been reached. + * + * The caller is not required to consume all of the data + * returned, i.e. &consumed is typically set to the number + * of bytes already consumed and the next call to + * skb_seq_read() will return the remaining part of the block. + * + * Note: The size of each block of data returned can be arbitary, + * this limitation is the cost for zerocopy seqeuental + * reads of potentially non linear data. + * + * Note: Fragment lists within fragments are not implemented + * at the moment, state->root_skb could be replaced with + * a stack for this purpose. + */ +unsigned int skb_seq_read(unsigned int consumed, const u8 **data, + struct skb_seq_state *st) +{ + unsigned int block_limit, abs_offset = consumed + st->lower_offset; + skb_frag_t *frag; + + if (unlikely(abs_offset >= st->upper_offset)) + return 0; + +next_skb: + block_limit = skb_headlen(st->cur_skb); + + if (abs_offset < block_limit) { + *data = st->cur_skb->data + abs_offset; + return block_limit - abs_offset; + } + + if (st->frag_idx == 0 && !st->frag_data) + st->stepped_offset += skb_headlen(st->cur_skb); + + while (st->frag_idx < skb_shinfo(st->cur_skb)->nr_frags) { + frag = &skb_shinfo(st->cur_skb)->frags[st->frag_idx]; + block_limit = frag->size + st->stepped_offset; + + if (abs_offset < block_limit) { + if (!st->frag_data) + st->frag_data = kmap_skb_frag(frag); + + *data = (u8 *) st->frag_data + frag->page_offset + + (abs_offset - st->stepped_offset); + + return block_limit - abs_offset; + } + + if (st->frag_data) { + kunmap_skb_frag(st->frag_data); + st->frag_data = NULL; + } + + st->frag_idx++; + st->stepped_offset += frag->size; + } + + if (st->cur_skb->next) { + st->cur_skb = st->cur_skb->next; + st->frag_idx = 0; + goto next_skb; + } else if (st->root_skb == st->cur_skb && + skb_shinfo(st->root_skb)->frag_list) { + st->cur_skb = skb_shinfo(st->root_skb)->frag_list; + goto next_skb; + } + + return 0; +} + +/** + * skb_abort_seq_read - Abort a sequential read of skb data + * @st: state variable + * + * Must be called if skb_seq_read() was not called until it + * returned 0. + */ +void skb_abort_seq_read(struct skb_seq_state *st) +{ + if (st->frag_data) + kunmap_skb_frag(st->frag_data); +} + +#define TS_SKB_CB(state) ((struct skb_seq_state *) &((state)->cb)) + +static unsigned int skb_ts_get_next_block(unsigned int offset, const u8 **text, + struct ts_config *conf, + struct ts_state *state) +{ + return skb_seq_read(offset, text, TS_SKB_CB(state)); +} + +static void skb_ts_finish(struct ts_config *conf, struct ts_state *state) +{ + skb_abort_seq_read(TS_SKB_CB(state)); +} + +/** + * skb_find_text - Find a text pattern in skb data + * @skb: the buffer to look in + * @from: search offset + * @to: search limit + * @config: textsearch configuration + * @state: uninitialized textsearch state variable + * + * Finds a pattern in the skb data according to the specified + * textsearch configuration. Use textsearch_next() to retrieve + * subsequent occurrences of the pattern. Returns the offset + * to the first occurrence or UINT_MAX if no match was found. + */ +unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, + unsigned int to, struct ts_config *config, + struct ts_state *state) +{ + config->get_next_block = skb_ts_get_next_block; + config->finish = skb_ts_finish; + + skb_prepare_seq_read(skb, from, to, TS_SKB_CB(state)); + + return textsearch_find(config, state); +} + void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", @@ -1538,3 +1691,7 @@ EXPORT_SYMBOL(skb_queue_tail); EXPORT_SYMBOL(skb_unlink); EXPORT_SYMBOL(skb_append); EXPORT_SYMBOL(skb_split); +EXPORT_SYMBOL(skb_prepare_seq_read); +EXPORT_SYMBOL(skb_seq_read); +EXPORT_SYMBOL(skb_abort_seq_read); +EXPORT_SYMBOL(skb_find_text); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index c8be646cb191..8f817ad9f546 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -13,12 +13,8 @@ #ifdef CONFIG_SYSCTL extern int netdev_max_backlog; +extern int netdev_budget; extern int weight_p; -extern int no_cong_thresh; -extern int no_cong; -extern int lo_cong; -extern int mod_cong; -extern int netdev_fastroute; extern int net_msg_cost; extern int net_msg_burst; @@ -35,19 +31,6 @@ extern int sysctl_somaxconn; extern char sysctl_divert_version[]; #endif /* CONFIG_NET_DIVERT */ -/* - * This strdup() is used for creating copies of network - * device names to be handed over to sysctl. - */ - -char *net_sysctl_strdup(const char *s) -{ - char *rv = kmalloc(strlen(s)+1, GFP_KERNEL); - if (rv) - strcpy(rv, s); - return rv; -} - ctl_table core_table[] = { #ifdef CONFIG_NET { @@ -98,38 +81,6 @@ ctl_table core_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, - { - .ctl_name = NET_CORE_NO_CONG_THRESH, - .procname = "no_cong_thresh", - .data = &no_cong_thresh, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = NET_CORE_NO_CONG, - .procname = "no_cong", - .data = &no_cong, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = NET_CORE_LO_CONG, - .procname = "lo_cong", - .data = &lo_cong, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = NET_CORE_MOD_CONG, - .procname = "mod_cong", - .data = &mod_cong, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, { .ctl_name = NET_CORE_MSG_COST, .procname = "message_cost", @@ -174,9 +125,15 @@ ctl_table core_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, + { + .ctl_name = NET_CORE_BUDGET, + .procname = "netdev_budget", + .data = &netdev_budget, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .ctl_name = 0 } }; -EXPORT_SYMBOL(net_sysctl_strdup); - #endif diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 567b03b1c349..690e88ba2484 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -433,5 +433,95 @@ config IP_TCPDIAG config IP_TCPDIAG_IPV6 def_bool (IP_TCPDIAG=y && IPV6=y) || (IP_TCPDIAG=m && IPV6) +# TCP Reno is builtin (required as fallback) +menu "TCP congestion control" + depends on INET + +config TCP_CONG_BIC + tristate "Binary Increase Congestion (BIC) control" + depends on INET + default y + ---help--- + BIC-TCP is a sender-side only change that ensures a linear RTT + fairness under large windows while offering both scalability and + bounded TCP-friendliness. The protocol combines two schemes + called additive increase and binary search increase. When the + congestion window is large, additive increase with a large + increment ensures linear RTT fairness as well as good + scalability. Under small congestion windows, binary search + increase provides TCP friendliness. + See http://www.csc.ncsu.edu/faculty/rhee/export/bitcp/ + +config TCP_CONG_WESTWOOD + tristate "TCP Westwood+" + depends on INET + default m + ---help--- + TCP Westwood+ is a sender-side only modification of the TCP Reno + protocol stack that optimizes the performance of TCP congestion + control. It is based on end-to-end bandwidth estimation to set + congestion window and slow start threshold after a congestion + episode. Using this estimation, TCP Westwood+ adaptively sets a + slow start threshold and a congestion window which takes into + account the bandwidth used at the time congestion is experienced. + TCP Westwood+ significantly increases fairness wrt TCP Reno in + wired networks and throughput over wireless links. + +config TCP_CONG_HTCP + tristate "H-TCP" + depends on INET + default m + ---help--- + H-TCP is a send-side only modifications of the TCP Reno + protocol stack that optimizes the performance of TCP + congestion control for high speed network links. It uses a + modeswitch to change the alpha and beta parameters of TCP Reno + based on network conditions and in a way so as to be fair with + other Reno and H-TCP flows. + +config TCP_CONG_HSTCP + tristate "High Speed TCP" + depends on INET && EXPERIMENTAL + default n + ---help--- + Sally Floyd's High Speed TCP (RFC 3649) congestion control. + A modification to TCP's congestion control mechanism for use + with large congestion windows. A table indicates how much to + increase the congestion window by when an ACK is received. + For more detail see http://www.icir.org/floyd/hstcp.html + +config TCP_CONG_HYBLA + tristate "TCP-Hybla congestion control algorithm" + depends on INET && EXPERIMENTAL + default n + ---help--- + TCP-Hybla is a sender-side only change that eliminates penalization of + long-RTT, large-bandwidth connections, like when satellite legs are + involved, expecially when sharing a common bottleneck with normal + terrestrial connections. + +config TCP_CONG_VEGAS + tristate "TCP Vegas" + depends on INET && EXPERIMENTAL + default n + ---help--- + TCP Vegas is a sender-side only change to TCP that anticipates + the onset of congestion by estimating the bandwidth. TCP Vegas + adjusts the sending rate by modifying the congestion + window. TCP Vegas should provide less packet loss, but it is + not as aggressive as TCP Reno. + +config TCP_CONG_SCALABLE + tristate "Scalable TCP" + depends on INET && EXPERIMENTAL + default n + ---help--- + Scalable TCP is a sender-side only change to TCP which uses a + MIMD congestion control algorithm which has some nice scaling + properties, though is known to have fairness issues. + See http://www-lce.eng.cam.ac.uk/~ctk21/scalable/ + +endmenu + source "net/ipv4/ipvs/Kconfig" diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 65d57d8e1add..5718cdb3a61e 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -5,7 +5,8 @@ obj-y := utils.o route.o inetpeer.o protocol.o \ ip_input.o ip_fragment.o ip_forward.o ip_options.o \ ip_output.o ip_sockglue.o \ - tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o tcp_minisocks.o \ + tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \ + tcp_minisocks.o tcp_cong.o \ datagram.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \ sysctl_net_ipv4.o fib_frontend.o fib_semantics.o @@ -30,6 +31,13 @@ obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_IP_VS) += ipvs/ obj-$(CONFIG_IP_TCPDIAG) += tcp_diag.o obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o +obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o +obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o +obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_highspeed.o +obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o +obj-$(CONFIG_TCP_CONG_HTCP) += tcp_htcp.o +obj-$(CONFIG_TCP_CONG_VEGAS) += tcp_vegas.o +obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ xfrm4_output.o diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 650dcb12d9a1..d8a10e3dd77d 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1471,7 +1471,7 @@ static void devinet_sysctl_register(struct in_device *in_dev, * by sysctl and we wouldn't want anyone to change it under our feet * (see SIOCSIFNAME). */ - dev_name = net_sysctl_strdup(dev_name); + dev_name = kstrdup(dev_name, GFP_KERNEL); if (!dev_name) goto free; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 23068bddbf0b..e32894532416 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -118,6 +118,45 @@ static int ipv4_sysctl_forward_strategy(ctl_table *table, return 1; } +static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + char val[TCP_CA_NAME_MAX]; + ctl_table tbl = { + .data = val, + .maxlen = TCP_CA_NAME_MAX, + }; + int ret; + + tcp_get_default_congestion_control(val); + + ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos); + if (write && ret == 0) + ret = tcp_set_default_congestion_control(val); + return ret; +} + +int sysctl_tcp_congestion_control(ctl_table *table, int __user *name, int nlen, + void __user *oldval, size_t __user *oldlenp, + void __user *newval, size_t newlen, + void **context) +{ + char val[TCP_CA_NAME_MAX]; + ctl_table tbl = { + .data = val, + .maxlen = TCP_CA_NAME_MAX, + }; + int ret; + + tcp_get_default_congestion_control(val); + ret = sysctl_string(&tbl, name, nlen, oldval, oldlenp, newval, newlen, + context); + if (ret == 0 && newval && newlen) + ret = tcp_set_default_congestion_control(val); + return ret; +} + + ctl_table ipv4_table[] = { { .ctl_name = NET_IPV4_TCP_TIMESTAMPS, @@ -611,70 +650,6 @@ ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, - { - .ctl_name = NET_TCP_WESTWOOD, - .procname = "tcp_westwood", - .data = &sysctl_tcp_westwood, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_VEGAS, - .procname = "tcp_vegas_cong_avoid", - .data = &sysctl_tcp_vegas_cong_avoid, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_VEGAS_ALPHA, - .procname = "tcp_vegas_alpha", - .data = &sysctl_tcp_vegas_alpha, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_VEGAS_BETA, - .procname = "tcp_vegas_beta", - .data = &sysctl_tcp_vegas_beta, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_VEGAS_GAMMA, - .procname = "tcp_vegas_gamma", - .data = &sysctl_tcp_vegas_gamma, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_BIC, - .procname = "tcp_bic", - .data = &sysctl_tcp_bic, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_BIC_FAST_CONVERGENCE, - .procname = "tcp_bic_fast_convergence", - .data = &sysctl_tcp_bic_fast_convergence, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = NET_TCP_BIC_LOW_WINDOW, - .procname = "tcp_bic_low_window", - .data = &sysctl_tcp_bic_low_window, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, { .ctl_name = NET_TCP_MODERATE_RCVBUF, .procname = "tcp_moderate_rcvbuf", @@ -692,13 +667,14 @@ ctl_table ipv4_table[] = { .proc_handler = &proc_dointvec, }, { - .ctl_name = NET_TCP_BIC_BETA, - .procname = "tcp_bic_beta", - .data = &sysctl_tcp_bic_beta, - .maxlen = sizeof(int), + .ctl_name = NET_TCP_CONG_CONTROL, + .procname = "tcp_congestion_control", .mode = 0644, - .proc_handler = &proc_dointvec, + .maxlen = TCP_CA_NAME_MAX, + .proc_handler = &proc_tcp_congestion_control, + .strategy = &sysctl_tcp_congestion_control, }, + { .ctl_name = 0 } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 674bbd8cfd36..882436da9a3a 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1927,6 +1927,25 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, return tp->af_specific->setsockopt(sk, level, optname, optval, optlen); + /* This is a string value all the others are int's */ + if (optname == TCP_CONGESTION) { + char name[TCP_CA_NAME_MAX]; + + if (optlen < 1) + return -EINVAL; + + val = strncpy_from_user(name, optval, + min(TCP_CA_NAME_MAX-1, optlen)); + if (val < 0) + return -EFAULT; + name[val] = 0; + + lock_sock(sk); + err = tcp_set_congestion_control(tp, name); + release_sock(sk); + return err; + } + if (optlen < sizeof(int)) return -EINVAL; @@ -2211,6 +2230,16 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, case TCP_QUICKACK: val = !tp->ack.pingpong; break; + + case TCP_CONGESTION: + if (get_user(len, optlen)) + return -EFAULT; + len = min_t(unsigned int, len, TCP_CA_NAME_MAX); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, tp->ca_ops->name, len)) + return -EFAULT; + return 0; default: return -ENOPROTOOPT; }; @@ -2224,7 +2253,7 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, extern void __skb_cb_too_small_for_tcp(int, int); -extern void tcpdiag_init(void); +extern struct tcp_congestion_ops tcp_reno; static __initdata unsigned long thash_entries; static int __init set_thash_entries(char *str) @@ -2333,6 +2362,8 @@ void __init tcp_init(void) printk(KERN_INFO "TCP: Hash tables configured " "(established %d bind %d)\n", tcp_ehash_size << 1, tcp_bhash_size); + + tcp_register_congestion_control(&tcp_reno); } EXPORT_SYMBOL(tcp_accept); diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c new file mode 100644 index 000000000000..ec38d45d6649 --- /dev/null +++ b/net/ipv4/tcp_bic.c @@ -0,0 +1,331 @@ +/* + * Binary Increase Congestion control for TCP + * + * This is from the implementation of BICTCP in + * Lison-Xu, Kahaled Harfoush, and Injong Rhee. + * "Binary Increase Congestion Control for Fast, Long Distance + * Networks" in InfoComm 2004 + * Available from: + * http://www.csc.ncsu.edu/faculty/rhee/export/bitcp.pdf + * + * Unless BIC is enabled and congestion window is large + * this behaves the same as the original Reno. + */ + +#include +#include +#include +#include + + +#define BICTCP_BETA_SCALE 1024 /* Scale factor beta calculation + * max_cwnd = snd_cwnd * beta + */ +#define BICTCP_B 4 /* + * In binary search, + * go to point (max+min)/N + */ + +static int fast_convergence = 1; +static int max_increment = 32; +static int low_window = 14; +static int beta = 819; /* = 819/1024 (BICTCP_BETA_SCALE) */ +static int low_utilization_threshold = 153; +static int low_utilization_period = 2; +static int initial_ssthresh = 100; +static int smooth_part = 20; + +module_param(fast_convergence, int, 0644); +MODULE_PARM_DESC(fast_convergence, "turn on/off fast convergence"); +module_param(max_increment, int, 0644); +MODULE_PARM_DESC(max_increment, "Limit on increment allowed during binary search"); +module_param(low_window, int, 0644); +MODULE_PARM_DESC(low_window, "lower bound on congestion window (for TCP friendliness)"); +module_param(beta, int, 0644); +MODULE_PARM_DESC(beta, "beta for multiplicative increase"); +module_param(low_utilization_threshold, int, 0644); +MODULE_PARM_DESC(low_utilization_threshold, "percent (scaled by 1024) for low utilization mode"); +module_param(low_utilization_period, int, 0644); +MODULE_PARM_DESC(low_utilization_period, "if average delay exceeds then goto to low utilization mode (seconds)"); +module_param(initial_ssthresh, int, 0644); +MODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold"); +module_param(smooth_part, int, 0644); +MODULE_PARM_DESC(smooth_part, "log(B/(B*Smin))/log(B/(B-1))+B, # of RTT from Wmax-B to Wmax"); + + +/* BIC TCP Parameters */ +struct bictcp { + u32 cnt; /* increase cwnd by 1 after ACKs */ + u32 last_max_cwnd; /* last maximum snd_cwnd */ + u32 loss_cwnd; /* congestion window at last loss */ + u32 last_cwnd; /* the last snd_cwnd */ + u32 last_time; /* time when updated last_cwnd */ + u32 delay_min; /* min delay */ + u32 delay_max; /* max delay */ + u32 last_delay; + u8 low_utilization;/* 0: high; 1: low */ + u32 low_utilization_start; /* starting time of low utilization detection*/ + u32 epoch_start; /* beginning of an epoch */ +#define ACK_RATIO_SHIFT 4 + u32 delayed_ack; /* estimate the ratio of Packets/ACKs << 4 */ +}; + +static inline void bictcp_reset(struct bictcp *ca) +{ + ca->cnt = 0; + ca->last_max_cwnd = 0; + ca->loss_cwnd = 0; + ca->last_cwnd = 0; + ca->last_time = 0; + ca->delay_min = 0; + ca->delay_max = 0; + ca->last_delay = 0; + ca->low_utilization = 0; + ca->low_utilization_start = 0; + ca->epoch_start = 0; + ca->delayed_ack = 2 << ACK_RATIO_SHIFT; +} + +static void bictcp_init(struct tcp_sock *tp) +{ + bictcp_reset(tcp_ca(tp)); + if (initial_ssthresh) + tp->snd_ssthresh = initial_ssthresh; +} + +/* + * Compute congestion window to use. + */ +static inline void bictcp_update(struct bictcp *ca, u32 cwnd) +{ + if (ca->last_cwnd == cwnd && + (s32)(tcp_time_stamp - ca->last_time) <= HZ / 32) + return; + + ca->last_cwnd = cwnd; + ca->last_time = tcp_time_stamp; + + if (ca->epoch_start == 0) /* record the beginning of an epoch */ + ca->epoch_start = tcp_time_stamp; + + /* start off normal */ + if (cwnd <= low_window) { + ca->cnt = cwnd; + return; + } + + /* binary increase */ + if (cwnd < ca->last_max_cwnd) { + __u32 dist = (ca->last_max_cwnd - cwnd) + / BICTCP_B; + + if (dist > max_increment) + /* linear increase */ + ca->cnt = cwnd / max_increment; + else if (dist <= 1U) + /* binary search increase */ + ca->cnt = (cwnd * smooth_part) / BICTCP_B; + else + /* binary search increase */ + ca->cnt = cwnd / dist; + } else { + /* slow start AMD linear increase */ + if (cwnd < ca->last_max_cwnd + BICTCP_B) + /* slow start */ + ca->cnt = (cwnd * smooth_part) / BICTCP_B; + else if (cwnd < ca->last_max_cwnd + max_increment*(BICTCP_B-1)) + /* slow start */ + ca->cnt = (cwnd * (BICTCP_B-1)) + / cwnd-ca->last_max_cwnd; + else + /* linear increase */ + ca->cnt = cwnd / max_increment; + } + + /* if in slow start or link utilization is very low */ + if ( ca->loss_cwnd == 0 || + (cwnd > ca->loss_cwnd && ca->low_utilization)) { + if (ca->cnt > 20) /* increase cwnd 5% per RTT */ + ca->cnt = 20; + } + + ca->cnt = (ca->cnt << ACK_RATIO_SHIFT) / ca->delayed_ack; + if (ca->cnt == 0) /* cannot be zero */ + ca->cnt = 1; +} + + +/* Detect low utilization in congestion avoidance */ +static inline void bictcp_low_utilization(struct tcp_sock *tp, int flag) +{ + struct bictcp *ca = tcp_ca(tp); + u32 dist, delay; + + /* No time stamp */ + if (!(tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) || + /* Discard delay samples right after fast recovery */ + tcp_time_stamp < ca->epoch_start + HZ || + /* this delay samples may not be accurate */ + flag == 0) { + ca->last_delay = 0; + goto notlow; + } + + delay = ca->last_delay<<3; /* use the same scale as tp->srtt*/ + ca->last_delay = tcp_time_stamp - tp->rx_opt.rcv_tsecr; + if (delay == 0) /* no previous delay sample */ + goto notlow; + + /* first time call or link delay decreases */ + if (ca->delay_min == 0 || ca->delay_min > delay) { + ca->delay_min = ca->delay_max = delay; + goto notlow; + } + + if (ca->delay_max < delay) + ca->delay_max = delay; + + /* utilization is low, if avg delay < dist*threshold + for checking_period time */ + dist = ca->delay_max - ca->delay_min; + if (dist <= ca->delay_min>>6 || + tp->srtt - ca->delay_min >= (dist*low_utilization_threshold)>>10) + goto notlow; + + if (ca->low_utilization_start == 0) { + ca->low_utilization = 0; + ca->low_utilization_start = tcp_time_stamp; + } else if ((s32)(tcp_time_stamp - ca->low_utilization_start) + > low_utilization_period*HZ) { + ca->low_utilization = 1; + } + + return; + + notlow: + ca->low_utilization = 0; + ca->low_utilization_start = 0; + +} + +static void bictcp_cong_avoid(struct tcp_sock *tp, u32 ack, + u32 seq_rtt, u32 in_flight, int data_acked) +{ + struct bictcp *ca = tcp_ca(tp); + + bictcp_low_utilization(tp, data_acked); + + if (in_flight < tp->snd_cwnd) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* In "safe" area, increase. */ + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + } else { + bictcp_update(ca, tp->snd_cwnd); + + /* In dangerous area, increase slowly. + * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd + */ + if (tp->snd_cwnd_cnt >= ca->cnt) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } else + tp->snd_cwnd_cnt++; + } + +} + +/* + * behave like Reno until low_window is reached, + * then increase congestion window slowly + */ +static u32 bictcp_recalc_ssthresh(struct tcp_sock *tp) +{ + struct bictcp *ca = tcp_ca(tp); + + ca->epoch_start = 0; /* end of epoch */ + + /* in case of wrong delay_max*/ + if (ca->delay_min > 0 && ca->delay_max > ca->delay_min) + ca->delay_max = ca->delay_min + + ((ca->delay_max - ca->delay_min)* 90) / 100; + + /* Wmax and fast convergence */ + if (tp->snd_cwnd < ca->last_max_cwnd && fast_convergence) + ca->last_max_cwnd = (tp->snd_cwnd * (BICTCP_BETA_SCALE + beta)) + / (2 * BICTCP_BETA_SCALE); + else + ca->last_max_cwnd = tp->snd_cwnd; + + ca->loss_cwnd = tp->snd_cwnd; + + + if (tp->snd_cwnd <= low_window) + return max(tp->snd_cwnd >> 1U, 2U); + else + return max((tp->snd_cwnd * beta) / BICTCP_BETA_SCALE, 2U); +} + +static u32 bictcp_undo_cwnd(struct tcp_sock *tp) +{ + struct bictcp *ca = tcp_ca(tp); + + return max(tp->snd_cwnd, ca->last_max_cwnd); +} + +static u32 bictcp_min_cwnd(struct tcp_sock *tp) +{ + return tp->snd_ssthresh; +} + +static void bictcp_state(struct tcp_sock *tp, u8 new_state) +{ + if (new_state == TCP_CA_Loss) + bictcp_reset(tcp_ca(tp)); +} + +/* Track delayed acknowledgement ratio using sliding window + * ratio = (15*ratio + sample) / 16 + */ +static void bictcp_acked(struct tcp_sock *tp, u32 cnt) +{ + if (cnt > 0 && tp->ca_state == TCP_CA_Open) { + struct bictcp *ca = tcp_ca(tp); + cnt -= ca->delayed_ack >> ACK_RATIO_SHIFT; + ca->delayed_ack += cnt; + } +} + + +static struct tcp_congestion_ops bictcp = { + .init = bictcp_init, + .ssthresh = bictcp_recalc_ssthresh, + .cong_avoid = bictcp_cong_avoid, + .set_state = bictcp_state, + .undo_cwnd = bictcp_undo_cwnd, + .min_cwnd = bictcp_min_cwnd, + .pkts_acked = bictcp_acked, + .owner = THIS_MODULE, + .name = "bic", +}; + +static int __init bictcp_register(void) +{ + BUG_ON(sizeof(struct bictcp) > TCP_CA_PRIV_SIZE); + return tcp_register_congestion_control(&bictcp); +} + +static void __exit bictcp_unregister(void) +{ + tcp_unregister_congestion_control(&bictcp); +} + +module_init(bictcp_register); +module_exit(bictcp_unregister); + +MODULE_AUTHOR("Stephen Hemminger"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("BIC TCP"); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c new file mode 100644 index 000000000000..4970d10a7785 --- /dev/null +++ b/net/ipv4/tcp_cong.c @@ -0,0 +1,237 @@ +/* + * Plugable TCP congestion control support and newReno + * congestion control. + * Based on ideas from I/O scheduler suport and Web100. + * + * Copyright (C) 2005 Stephen Hemminger + */ + +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(tcp_cong_list_lock); +static LIST_HEAD(tcp_cong_list); + +/* Simple linear search, don't expect many entries! */ +static struct tcp_congestion_ops *tcp_ca_find(const char *name) +{ + struct tcp_congestion_ops *e; + + list_for_each_entry_rcu(e, &tcp_cong_list, list) { + if (strcmp(e->name, name) == 0) + return e; + } + + return NULL; +} + +/* + * Attach new congestion control algorthim to the list + * of available options. + */ +int tcp_register_congestion_control(struct tcp_congestion_ops *ca) +{ + int ret = 0; + + /* all algorithms must implement ssthresh and cong_avoid ops */ + if (!ca->ssthresh || !ca->cong_avoid || !ca->min_cwnd) { + printk(KERN_ERR "TCP %s does not implement required ops\n", + ca->name); + return -EINVAL; + } + + spin_lock(&tcp_cong_list_lock); + if (tcp_ca_find(ca->name)) { + printk(KERN_NOTICE "TCP %s already registered\n", ca->name); + ret = -EEXIST; + } else { + list_add_rcu(&ca->list, &tcp_cong_list); + printk(KERN_INFO "TCP %s registered\n", ca->name); + } + spin_unlock(&tcp_cong_list_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(tcp_register_congestion_control); + +/* + * Remove congestion control algorithm, called from + * the module's remove function. Module ref counts are used + * to ensure that this can't be done till all sockets using + * that method are closed. + */ +void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca) +{ + spin_lock(&tcp_cong_list_lock); + list_del_rcu(&ca->list); + spin_unlock(&tcp_cong_list_lock); +} +EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control); + +/* Assign choice of congestion control. */ +void tcp_init_congestion_control(struct tcp_sock *tp) +{ + struct tcp_congestion_ops *ca; + + if (tp->ca_ops != &tcp_init_congestion_ops) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(ca, &tcp_cong_list, list) { + if (try_module_get(ca->owner)) { + tp->ca_ops = ca; + break; + } + + } + rcu_read_unlock(); + + if (tp->ca_ops->init) + tp->ca_ops->init(tp); +} + +/* Manage refcounts on socket close. */ +void tcp_cleanup_congestion_control(struct tcp_sock *tp) +{ + if (tp->ca_ops->release) + tp->ca_ops->release(tp); + module_put(tp->ca_ops->owner); +} + +/* Used by sysctl to change default congestion control */ +int tcp_set_default_congestion_control(const char *name) +{ + struct tcp_congestion_ops *ca; + int ret = -ENOENT; + + spin_lock(&tcp_cong_list_lock); + ca = tcp_ca_find(name); +#ifdef CONFIG_KMOD + if (!ca) { + spin_unlock(&tcp_cong_list_lock); + + request_module("tcp_%s", name); + spin_lock(&tcp_cong_list_lock); + ca = tcp_ca_find(name); + } +#endif + + if (ca) { + list_move(&ca->list, &tcp_cong_list); + ret = 0; + } + spin_unlock(&tcp_cong_list_lock); + + return ret; +} + +/* Get current default congestion control */ +void tcp_get_default_congestion_control(char *name) +{ + struct tcp_congestion_ops *ca; + /* We will always have reno... */ + BUG_ON(list_empty(&tcp_cong_list)); + + rcu_read_lock(); + ca = list_entry(tcp_cong_list.next, struct tcp_congestion_ops, list); + strncpy(name, ca->name, TCP_CA_NAME_MAX); + rcu_read_unlock(); +} + +/* Change congestion control for socket */ +int tcp_set_congestion_control(struct tcp_sock *tp, const char *name) +{ + struct tcp_congestion_ops *ca; + int err = 0; + + rcu_read_lock(); + ca = tcp_ca_find(name); + if (ca == tp->ca_ops) + goto out; + + if (!ca) + err = -ENOENT; + + else if (!try_module_get(ca->owner)) + err = -EBUSY; + + else { + tcp_cleanup_congestion_control(tp); + tp->ca_ops = ca; + if (tp->ca_ops->init) + tp->ca_ops->init(tp); + } + out: + rcu_read_unlock(); + return err; +} + +/* + * TCP Reno congestion control + * This is special case used for fallback as well. + */ +/* This is Jacobson's slow start and congestion avoidance. + * SIGCOMM '88, p. 328. + */ +void tcp_reno_cong_avoid(struct tcp_sock *tp, u32 ack, u32 rtt, u32 in_flight, + int flag) +{ + if (in_flight < tp->snd_cwnd) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* In "safe" area, increase. */ + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + } else { + /* In dangerous area, increase slowly. + * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd + */ + if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } else + tp->snd_cwnd_cnt++; + } +} +EXPORT_SYMBOL_GPL(tcp_reno_cong_avoid); + +/* Slow start threshold is half the congestion window (min 2) */ +u32 tcp_reno_ssthresh(struct tcp_sock *tp) +{ + return max(tp->snd_cwnd >> 1U, 2U); +} +EXPORT_SYMBOL_GPL(tcp_reno_ssthresh); + +/* Lower bound on congestion window. */ +u32 tcp_reno_min_cwnd(struct tcp_sock *tp) +{ + return tp->snd_ssthresh/2; +} +EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd); + +struct tcp_congestion_ops tcp_reno = { + .name = "reno", + .owner = THIS_MODULE, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = tcp_reno_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, +}; + +/* Initial congestion control used (until SYN) + * really reno under another name so we can tell difference + * during tcp_set_default_congestion_control + */ +struct tcp_congestion_ops tcp_init_congestion_ops = { + .name = "", + .owner = THIS_MODULE, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = tcp_reno_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, +}; +EXPORT_SYMBOL_GPL(tcp_init_congestion_ops); diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 634befc07921..f66945cb158f 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -42,15 +42,8 @@ struct tcpdiag_entry static struct sock *tcpnl; - #define TCPDIAG_PUT(skb, attrtype, attrlen) \ -({ int rtalen = RTA_LENGTH(attrlen); \ - struct rtattr *rta; \ - if (skb_tailroom(skb) < RTA_ALIGN(rtalen)) goto nlmsg_failure; \ - rta = (void*)__skb_put(skb, RTA_ALIGN(rtalen)); \ - rta->rta_type = attrtype; \ - rta->rta_len = rtalen; \ - RTA_DATA(rta); }) + RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, int ext, u32 pid, u32 seq, u16 nlmsg_flags) @@ -61,7 +54,6 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, struct nlmsghdr *nlh; struct tcp_info *info = NULL; struct tcpdiag_meminfo *minfo = NULL; - struct tcpvegas_info *vinfo = NULL; unsigned char *b = skb->tail; nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r)); @@ -73,9 +65,11 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, if (ext & (1<<(TCPDIAG_INFO-1))) info = TCPDIAG_PUT(skb, TCPDIAG_INFO, sizeof(*info)); - if ((tcp_is_westwood(tp) || tcp_is_vegas(tp)) - && (ext & (1<<(TCPDIAG_VEGASINFO-1)))) - vinfo = TCPDIAG_PUT(skb, TCPDIAG_VEGASINFO, sizeof(*vinfo)); + if (ext & (1<<(TCPDIAG_CONG-1))) { + size_t len = strlen(tp->ca_ops->name); + strcpy(TCPDIAG_PUT(skb, TCPDIAG_CONG, len+1), + tp->ca_ops->name); + } } r->tcpdiag_family = sk->sk_family; r->tcpdiag_state = sk->sk_state; @@ -166,23 +160,13 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, if (info) tcp_get_info(sk, info); - if (vinfo) { - if (tcp_is_vegas(tp)) { - vinfo->tcpv_enabled = tp->vegas.doing_vegas_now; - vinfo->tcpv_rttcnt = tp->vegas.cntRTT; - vinfo->tcpv_rtt = jiffies_to_usecs(tp->vegas.baseRTT); - vinfo->tcpv_minrtt = jiffies_to_usecs(tp->vegas.minRTT); - } else { - vinfo->tcpv_enabled = 0; - vinfo->tcpv_rttcnt = 0; - vinfo->tcpv_rtt = jiffies_to_usecs(tp->westwood.rtt); - vinfo->tcpv_minrtt = jiffies_to_usecs(tp->westwood.rtt_min); - } - } + if (sk->sk_state < TCP_TIME_WAIT && tp->ca_ops->get_info) + tp->ca_ops->get_info(tp, ext, skb); nlh->nlmsg_len = skb->tail - b; return skb->len; +rtattr_failure: nlmsg_failure: skb_trim(skb, b - skb->data); return -1; diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c new file mode 100644 index 000000000000..36c51f8136bf --- /dev/null +++ b/net/ipv4/tcp_highspeed.c @@ -0,0 +1,181 @@ +/* + * Sally Floyd's High Speed TCP (RFC 3649) congestion control + * + * See http://www.icir.org/floyd/hstcp.html + * + * John Heffner + */ + +#include +#include +#include + + +/* From AIMD tables from RFC 3649 appendix B, + * with fixed-point MD scaled <<8. + */ +static const struct hstcp_aimd_val { + unsigned int cwnd; + unsigned int md; +} hstcp_aimd_vals[] = { + { 38, 128, /* 0.50 */ }, + { 118, 112, /* 0.44 */ }, + { 221, 104, /* 0.41 */ }, + { 347, 98, /* 0.38 */ }, + { 495, 93, /* 0.37 */ }, + { 663, 89, /* 0.35 */ }, + { 851, 86, /* 0.34 */ }, + { 1058, 83, /* 0.33 */ }, + { 1284, 81, /* 0.32 */ }, + { 1529, 78, /* 0.31 */ }, + { 1793, 76, /* 0.30 */ }, + { 2076, 74, /* 0.29 */ }, + { 2378, 72, /* 0.28 */ }, + { 2699, 71, /* 0.28 */ }, + { 3039, 69, /* 0.27 */ }, + { 3399, 68, /* 0.27 */ }, + { 3778, 66, /* 0.26 */ }, + { 4177, 65, /* 0.26 */ }, + { 4596, 64, /* 0.25 */ }, + { 5036, 62, /* 0.25 */ }, + { 5497, 61, /* 0.24 */ }, + { 5979, 60, /* 0.24 */ }, + { 6483, 59, /* 0.23 */ }, + { 7009, 58, /* 0.23 */ }, + { 7558, 57, /* 0.22 */ }, + { 8130, 56, /* 0.22 */ }, + { 8726, 55, /* 0.22 */ }, + { 9346, 54, /* 0.21 */ }, + { 9991, 53, /* 0.21 */ }, + { 10661, 52, /* 0.21 */ }, + { 11358, 52, /* 0.20 */ }, + { 12082, 51, /* 0.20 */ }, + { 12834, 50, /* 0.20 */ }, + { 13614, 49, /* 0.19 */ }, + { 14424, 48, /* 0.19 */ }, + { 15265, 48, /* 0.19 */ }, + { 16137, 47, /* 0.19 */ }, + { 17042, 46, /* 0.18 */ }, + { 17981, 45, /* 0.18 */ }, + { 18955, 45, /* 0.18 */ }, + { 19965, 44, /* 0.17 */ }, + { 21013, 43, /* 0.17 */ }, + { 22101, 43, /* 0.17 */ }, + { 23230, 42, /* 0.17 */ }, + { 24402, 41, /* 0.16 */ }, + { 25618, 41, /* 0.16 */ }, + { 26881, 40, /* 0.16 */ }, + { 28193, 39, /* 0.16 */ }, + { 29557, 39, /* 0.15 */ }, + { 30975, 38, /* 0.15 */ }, + { 32450, 38, /* 0.15 */ }, + { 33986, 37, /* 0.15 */ }, + { 35586, 36, /* 0.14 */ }, + { 37253, 36, /* 0.14 */ }, + { 38992, 35, /* 0.14 */ }, + { 40808, 35, /* 0.14 */ }, + { 42707, 34, /* 0.13 */ }, + { 44694, 33, /* 0.13 */ }, + { 46776, 33, /* 0.13 */ }, + { 48961, 32, /* 0.13 */ }, + { 51258, 32, /* 0.13 */ }, + { 53677, 31, /* 0.12 */ }, + { 56230, 30, /* 0.12 */ }, + { 58932, 30, /* 0.12 */ }, + { 61799, 29, /* 0.12 */ }, + { 64851, 28, /* 0.11 */ }, + { 68113, 28, /* 0.11 */ }, + { 71617, 27, /* 0.11 */ }, + { 75401, 26, /* 0.10 */ }, + { 79517, 26, /* 0.10 */ }, + { 84035, 25, /* 0.10 */ }, + { 89053, 24, /* 0.10 */ }, +}; + +#define HSTCP_AIMD_MAX ARRAY_SIZE(hstcp_aimd_vals) + +struct hstcp { + u32 ai; +}; + +static void hstcp_init(struct tcp_sock *tp) +{ + struct hstcp *ca = tcp_ca(tp); + + ca->ai = 0; + + /* Ensure the MD arithmetic works. This is somewhat pedantic, + * since I don't think we will see a cwnd this large. :) */ + tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128); +} + +static void hstcp_cong_avoid(struct tcp_sock *tp, u32 adk, u32 rtt, + u32 in_flight, int good) +{ + struct hstcp *ca = tcp_ca(tp); + + if (in_flight < tp->snd_cwnd) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + } else { + /* Update AIMD parameters */ + if (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd) { + while (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd && + ca->ai < HSTCP_AIMD_MAX) + ca->ai++; + } else if (tp->snd_cwnd < hstcp_aimd_vals[ca->ai].cwnd) { + while (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd && + ca->ai > 0) + ca->ai--; + } + + /* Do additive increase */ + if (tp->snd_cwnd < tp->snd_cwnd_clamp) { + tp->snd_cwnd_cnt += ca->ai; + if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + tp->snd_cwnd++; + tp->snd_cwnd_cnt -= tp->snd_cwnd; + } + } + } +} + +static u32 hstcp_ssthresh(struct tcp_sock *tp) +{ + struct hstcp *ca = tcp_ca(tp); + + /* Do multiplicative decrease */ + return max(tp->snd_cwnd - ((tp->snd_cwnd * hstcp_aimd_vals[ca->ai].md) >> 8), 2U); +} + + +static struct tcp_congestion_ops tcp_highspeed = { + .init = hstcp_init, + .ssthresh = hstcp_ssthresh, + .cong_avoid = hstcp_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, + + .owner = THIS_MODULE, + .name = "highspeed" +}; + +static int __init hstcp_register(void) +{ + BUG_ON(sizeof(struct hstcp) > TCP_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_highspeed); +} + +static void __exit hstcp_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_highspeed); +} + +module_init(hstcp_register); +module_exit(hstcp_unregister); + +MODULE_AUTHOR("John Heffner"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("High Speed TCP"); diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c new file mode 100644 index 000000000000..40168275acf9 --- /dev/null +++ b/net/ipv4/tcp_htcp.c @@ -0,0 +1,289 @@ +/* + * H-TCP congestion control. The algorithm is detailed in: + * R.N.Shorten, D.J.Leith: + * "H-TCP: TCP for high-speed and long-distance networks" + * Proc. PFLDnet, Argonne, 2004. + * http://www.hamilton.ie/net/htcp3.pdf + */ + +#include +#include +#include +#include + +#define ALPHA_BASE (1<<7) /* 1.0 with shift << 7 */ +#define BETA_MIN (1<<6) /* 0.5 with shift << 7 */ +#define BETA_MAX 102 /* 0.8 with shift << 7 */ + +static int use_rtt_scaling = 1; +module_param(use_rtt_scaling, int, 0644); +MODULE_PARM_DESC(use_rtt_scaling, "turn on/off RTT scaling"); + +static int use_bandwidth_switch = 1; +module_param(use_bandwidth_switch, int, 0644); +MODULE_PARM_DESC(use_bandwidth_switch, "turn on/off bandwidth switcher"); + +struct htcp { + u16 alpha; /* Fixed point arith, << 7 */ + u8 beta; /* Fixed point arith, << 7 */ + u8 modeswitch; /* Delay modeswitch until we had at least one congestion event */ + u8 ccount; /* Number of RTTs since last congestion event */ + u8 undo_ccount; + u16 packetcount; + u32 minRTT; + u32 maxRTT; + u32 snd_cwnd_cnt2; + + u32 undo_maxRTT; + u32 undo_old_maxB; + + /* Bandwidth estimation */ + u32 minB; + u32 maxB; + u32 old_maxB; + u32 Bi; + u32 lasttime; +}; + +static inline void htcp_reset(struct htcp *ca) +{ + ca->undo_ccount = ca->ccount; + ca->undo_maxRTT = ca->maxRTT; + ca->undo_old_maxB = ca->old_maxB; + + ca->ccount = 0; + ca->snd_cwnd_cnt2 = 0; +} + +static u32 htcp_cwnd_undo(struct tcp_sock *tp) +{ + struct htcp *ca = tcp_ca(tp); + ca->ccount = ca->undo_ccount; + ca->maxRTT = ca->undo_maxRTT; + ca->old_maxB = ca->undo_old_maxB; + return max(tp->snd_cwnd, (tp->snd_ssthresh<<7)/ca->beta); +} + +static inline void measure_rtt(struct tcp_sock *tp) +{ + struct htcp *ca = tcp_ca(tp); + u32 srtt = tp->srtt>>3; + + /* keep track of minimum RTT seen so far, minRTT is zero at first */ + if (ca->minRTT > srtt || !ca->minRTT) + ca->minRTT = srtt; + + /* max RTT */ + if (tp->ca_state == TCP_CA_Open && tp->snd_ssthresh < 0xFFFF && ca->ccount > 3) { + if (ca->maxRTT < ca->minRTT) + ca->maxRTT = ca->minRTT; + if (ca->maxRTT < srtt && srtt <= ca->maxRTT+HZ/50) + ca->maxRTT = srtt; + } +} + +static void measure_achieved_throughput(struct tcp_sock *tp, u32 pkts_acked) +{ + struct htcp *ca = tcp_ca(tp); + u32 now = tcp_time_stamp; + + /* achieved throughput calculations */ + if (tp->ca_state != TCP_CA_Open && tp->ca_state != TCP_CA_Disorder) { + ca->packetcount = 0; + ca->lasttime = now; + return; + } + + ca->packetcount += pkts_acked; + + if (ca->packetcount >= tp->snd_cwnd - (ca->alpha>>7? : 1) + && now - ca->lasttime >= ca->minRTT + && ca->minRTT > 0) { + __u32 cur_Bi = ca->packetcount*HZ/(now - ca->lasttime); + if (ca->ccount <= 3) { + /* just after backoff */ + ca->minB = ca->maxB = ca->Bi = cur_Bi; + } else { + ca->Bi = (3*ca->Bi + cur_Bi)/4; + if (ca->Bi > ca->maxB) + ca->maxB = ca->Bi; + if (ca->minB > ca->maxB) + ca->minB = ca->maxB; + } + ca->packetcount = 0; + ca->lasttime = now; + } +} + +static inline void htcp_beta_update(struct htcp *ca, u32 minRTT, u32 maxRTT) +{ + if (use_bandwidth_switch) { + u32 maxB = ca->maxB; + u32 old_maxB = ca->old_maxB; + ca->old_maxB = ca->maxB; + + if (!between(5*maxB, 4*old_maxB, 6*old_maxB)) { + ca->beta = BETA_MIN; + ca->modeswitch = 0; + return; + } + } + + if (ca->modeswitch && minRTT > max(HZ/100, 1) && maxRTT) { + ca->beta = (minRTT<<7)/maxRTT; + if (ca->beta < BETA_MIN) + ca->beta = BETA_MIN; + else if (ca->beta > BETA_MAX) + ca->beta = BETA_MAX; + } else { + ca->beta = BETA_MIN; + ca->modeswitch = 1; + } +} + +static inline void htcp_alpha_update(struct htcp *ca) +{ + u32 minRTT = ca->minRTT; + u32 factor = 1; + u32 diff = ca->ccount * minRTT; /* time since last backoff */ + + if (diff > HZ) { + diff -= HZ; + factor = 1+ ( 10*diff + ((diff/2)*(diff/2)/HZ) )/HZ; + } + + if (use_rtt_scaling && minRTT) { + u32 scale = (HZ<<3)/(10*minRTT); + scale = min(max(scale, 1U<<2), 10U<<3); /* clamping ratio to interval [0.5,10]<<3 */ + factor = (factor<<3)/scale; + if (!factor) + factor = 1; + } + + ca->alpha = 2*factor*((1<<7)-ca->beta); + if (!ca->alpha) + ca->alpha = ALPHA_BASE; +} + +/* After we have the rtt data to calculate beta, we'd still prefer to wait one + * rtt before we adjust our beta to ensure we are working from a consistent + * data. + * + * This function should be called when we hit a congestion event since only at + * that point do we really have a real sense of maxRTT (the queues en route + * were getting just too full now). + */ +static void htcp_param_update(struct tcp_sock *tp) +{ + struct htcp *ca = tcp_ca(tp); + u32 minRTT = ca->minRTT; + u32 maxRTT = ca->maxRTT; + + htcp_beta_update(ca, minRTT, maxRTT); + htcp_alpha_update(ca); + + /* add slowly fading memory for maxRTT to accommodate routing changes etc */ + if (minRTT > 0 && maxRTT > minRTT) + ca->maxRTT = minRTT + ((maxRTT-minRTT)*95)/100; +} + +static u32 htcp_recalc_ssthresh(struct tcp_sock *tp) +{ + struct htcp *ca = tcp_ca(tp); + htcp_param_update(tp); + return max((tp->snd_cwnd * ca->beta) >> 7, 2U); +} + +static void htcp_cong_avoid(struct tcp_sock *tp, u32 ack, u32 rtt, + u32 in_flight, int data_acked) +{ + struct htcp *ca = tcp_ca(tp); + + if (in_flight < tp->snd_cwnd) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* In "safe" area, increase. */ + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + } else { + measure_rtt(tp); + + /* keep track of number of round-trip times since last backoff event */ + if (ca->snd_cwnd_cnt2++ > tp->snd_cwnd) { + ca->ccount++; + ca->snd_cwnd_cnt2 = 0; + htcp_alpha_update(ca); + } + + /* In dangerous area, increase slowly. + * In theory this is tp->snd_cwnd += alpha / tp->snd_cwnd + */ + if ((tp->snd_cwnd_cnt++ * ca->alpha)>>7 >= tp->snd_cwnd) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + ca->ccount++; + } + } +} + +/* Lower bound on congestion window. */ +static u32 htcp_min_cwnd(struct tcp_sock *tp) +{ + return tp->snd_ssthresh; +} + + +static void htcp_init(struct tcp_sock *tp) +{ + struct htcp *ca = tcp_ca(tp); + + memset(ca, 0, sizeof(struct htcp)); + ca->alpha = ALPHA_BASE; + ca->beta = BETA_MIN; +} + +static void htcp_state(struct tcp_sock *tp, u8 new_state) +{ + switch (new_state) { + case TCP_CA_CWR: + case TCP_CA_Recovery: + case TCP_CA_Loss: + htcp_reset(tcp_ca(tp)); + break; + } +} + +static struct tcp_congestion_ops htcp = { + .init = htcp_init, + .ssthresh = htcp_recalc_ssthresh, + .min_cwnd = htcp_min_cwnd, + .cong_avoid = htcp_cong_avoid, + .set_state = htcp_state, + .undo_cwnd = htcp_cwnd_undo, + .pkts_acked = measure_achieved_throughput, + .owner = THIS_MODULE, + .name = "htcp", +}; + +static int __init htcp_register(void) +{ + BUG_ON(sizeof(struct htcp) > TCP_CA_PRIV_SIZE); + BUILD_BUG_ON(BETA_MIN >= BETA_MAX); + if (!use_bandwidth_switch) + htcp.pkts_acked = NULL; + return tcp_register_congestion_control(&htcp); +} + +static void __exit htcp_unregister(void) +{ + tcp_unregister_congestion_control(&htcp); +} + +module_init(htcp_register); +module_exit(htcp_unregister); + +MODULE_AUTHOR("Baruch Even"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("H-TCP"); diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c new file mode 100644 index 000000000000..13a66342c304 --- /dev/null +++ b/net/ipv4/tcp_hybla.c @@ -0,0 +1,187 @@ +/* + * TCP HYBLA + * + * TCP-HYBLA Congestion control algorithm, based on: + * C.Caini, R.Firrincieli, "TCP-Hybla: A TCP Enhancement + * for Heterogeneous Networks", + * International Journal on satellite Communications, + * September 2004 + * Daniele Lacamera + * root at danielinux.net + */ + +#include +#include +#include + +/* Tcp Hybla structure. */ +struct hybla { + u8 hybla_en; + u32 snd_cwnd_cents; /* Keeps increment values when it is <1, <<7 */ + u32 rho; /* Rho parameter, integer part */ + u32 rho2; /* Rho * Rho, integer part */ + u32 rho_3ls; /* Rho parameter, <<3 */ + u32 rho2_7ls; /* Rho^2, <<7 */ + u32 minrtt; /* Minimum smoothed round trip time value seen */ +}; + +/* Hybla reference round trip time (default= 1/40 sec = 25 ms), + expressed in jiffies */ +static int rtt0 = 25; +module_param(rtt0, int, 0644); +MODULE_PARM_DESC(rtt0, "reference rout trip time (ms)"); + + +/* This is called to refresh values for hybla parameters */ +static inline void hybla_recalc_param (struct tcp_sock *tp) +{ + struct hybla *ca = tcp_ca(tp); + + ca->rho_3ls = max_t(u32, tp->srtt / msecs_to_jiffies(rtt0), 8); + ca->rho = ca->rho_3ls >> 3; + ca->rho2_7ls = (ca->rho_3ls * ca->rho_3ls) << 1; + ca->rho2 = ca->rho2_7ls >>7; +} + +static void hybla_init(struct tcp_sock *tp) +{ + struct hybla *ca = tcp_ca(tp); + + ca->rho = 0; + ca->rho2 = 0; + ca->rho_3ls = 0; + ca->rho2_7ls = 0; + ca->snd_cwnd_cents = 0; + ca->hybla_en = 1; + tp->snd_cwnd = 2; + tp->snd_cwnd_clamp = 65535; + + /* 1st Rho measurement based on initial srtt */ + hybla_recalc_param(tp); + + /* set minimum rtt as this is the 1st ever seen */ + ca->minrtt = tp->srtt; + tp->snd_cwnd = ca->rho; +} + +static void hybla_state(struct tcp_sock *tp, u8 ca_state) +{ + struct hybla *ca = tcp_ca(tp); + + ca->hybla_en = (ca_state == TCP_CA_Open); +} + +static inline u32 hybla_fraction(u32 odds) +{ + static const u32 fractions[] = { + 128, 139, 152, 165, 181, 197, 215, 234, + }; + + return (odds < ARRAY_SIZE(fractions)) ? fractions[odds] : 128; +} + +/* TCP Hybla main routine. + * This is the algorithm behavior: + * o Recalc Hybla parameters if min_rtt has changed + * o Give cwnd a new value based on the model proposed + * o remember increments <1 + */ +static void hybla_cong_avoid(struct tcp_sock *tp, u32 ack, u32 rtt, + u32 in_flight, int flag) +{ + struct hybla *ca = tcp_ca(tp); + u32 increment, odd, rho_fractions; + int is_slowstart = 0; + + /* Recalculate rho only if this srtt is the lowest */ + if (tp->srtt < ca->minrtt){ + hybla_recalc_param(tp); + ca->minrtt = tp->srtt; + } + + if (!ca->hybla_en) + return tcp_reno_cong_avoid(tp, ack, rtt, in_flight, flag); + + if (in_flight < tp->snd_cwnd) + return; + + if (ca->rho == 0) + hybla_recalc_param(tp); + + rho_fractions = ca->rho_3ls - (ca->rho << 3); + + if (tp->snd_cwnd < tp->snd_ssthresh) { + /* + * slow start + * INC = 2^RHO - 1 + * This is done by splitting the rho parameter + * into 2 parts: an integer part and a fraction part. + * Inrement<<7 is estimated by doing: + * [2^(int+fract)]<<7 + * that is equal to: + * (2^int) * [(2^fract) <<7] + * 2^int is straightly computed as 1<rho) * hybla_fraction(rho_fractions)) + - 128; + } else { + /* + * congestion avoidance + * INC = RHO^2 / W + * as long as increment is estimated as (rho<<7)/window + * it already is <<7 and we can easily count its fractions. + */ + increment = ca->rho2_7ls / tp->snd_cwnd; + if (increment < 128) + tp->snd_cwnd_cnt++; + } + + odd = increment % 128; + tp->snd_cwnd += increment >> 7; + ca->snd_cwnd_cents += odd; + + /* check when fractions goes >=128 and increase cwnd by 1. */ + while(ca->snd_cwnd_cents >= 128) { + tp->snd_cwnd++; + ca->snd_cwnd_cents -= 128; + tp->snd_cwnd_cnt = 0; + } + + /* clamp down slowstart cwnd to ssthresh value. */ + if (is_slowstart) + tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); + + tp->snd_cwnd = min_t(u32, tp->snd_cwnd, tp->snd_cwnd_clamp); +} + +static struct tcp_congestion_ops tcp_hybla = { + .init = hybla_init, + .ssthresh = tcp_reno_ssthresh, + .min_cwnd = tcp_reno_min_cwnd, + .cong_avoid = hybla_cong_avoid, + .set_state = hybla_state, + + .owner = THIS_MODULE, + .name = "hybla" +}; + +static int __init hybla_register(void) +{ + BUG_ON(sizeof(struct hybla) > TCP_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_hybla); +} + +static void __exit hybla_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_hybla); +} + +module_init(hybla_register); +module_exit(hybla_unregister); + +MODULE_AUTHOR("Daniele Lacamera"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Hybla"); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5bad504630a3..7bbbbc33eb4b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -61,7 +61,6 @@ * Panu Kuhlberg: Experimental audit of TCP (re)transmission * engine. Lots of bugs are found. * Pasi Sarolahti: F-RTO for dealing with spurious RTOs - * Angelo Dell'Aera: TCP Westwood+ support */ #include @@ -88,23 +87,9 @@ int sysctl_tcp_rfc1337; int sysctl_tcp_max_orphans = NR_FILE; int sysctl_tcp_frto; int sysctl_tcp_nometrics_save; -int sysctl_tcp_westwood; -int sysctl_tcp_vegas_cong_avoid; int sysctl_tcp_moderate_rcvbuf = 1; -/* Default values of the Vegas variables, in fixed-point representation - * with V_PARAM_SHIFT bits to the right of the binary point. - */ -#define V_PARAM_SHIFT 1 -int sysctl_tcp_vegas_alpha = 1<snd_cwnd_stamp = tcp_time_stamp; } -static void init_bictcp(struct tcp_sock *tp) -{ - tp->bictcp.cnt = 0; - - tp->bictcp.last_max_cwnd = 0; - tp->bictcp.last_cwnd = 0; - tp->bictcp.last_stamp = 0; -} - /* 5. Recalculate window clamp after socket hit its memory bounds. */ static void tcp_clamp_window(struct sock *sk, struct tcp_sock *tp) { @@ -558,45 +534,6 @@ static void tcp_event_data_recv(struct sock *sk, struct tcp_sock *tp, struct sk_ tcp_grow_window(sk, tp, skb); } -/* When starting a new connection, pin down the current choice of - * congestion algorithm. - */ -void tcp_ca_init(struct tcp_sock *tp) -{ - if (sysctl_tcp_westwood) - tp->adv_cong = TCP_WESTWOOD; - else if (sysctl_tcp_bic) - tp->adv_cong = TCP_BIC; - else if (sysctl_tcp_vegas_cong_avoid) { - tp->adv_cong = TCP_VEGAS; - tp->vegas.baseRTT = 0x7fffffff; - tcp_vegas_enable(tp); - } -} - -/* Do RTT sampling needed for Vegas. - * Basically we: - * o min-filter RTT samples from within an RTT to get the current - * propagation delay + queuing delay (we are min-filtering to try to - * avoid the effects of delayed ACKs) - * o min-filter RTT samples from a much longer window (forever for now) - * to find the propagation delay (baseRTT) - */ -static inline void vegas_rtt_calc(struct tcp_sock *tp, __u32 rtt) -{ - __u32 vrtt = rtt + 1; /* Never allow zero rtt or baseRTT */ - - /* Filter to find propagation delay: */ - if (vrtt < tp->vegas.baseRTT) - tp->vegas.baseRTT = vrtt; - - /* Find the min RTT during the last RTT to find - * the current prop. delay + queuing delay: - */ - tp->vegas.minRTT = min(tp->vegas.minRTT, vrtt); - tp->vegas.cntRTT++; -} - /* Called to compute a smoothed rtt estimate. The data fed to this * routine either comes from timestamps, or from segments that were * known _not_ to have been retransmitted [see Karn/Partridge @@ -606,13 +543,10 @@ static inline void vegas_rtt_calc(struct tcp_sock *tp, __u32 rtt) * To save cycles in the RFC 1323 implementation it was better to break * it up into three procedures. -- erics */ -static void tcp_rtt_estimator(struct tcp_sock *tp, __u32 mrtt) +static void tcp_rtt_estimator(struct tcp_sock *tp, __u32 mrtt, u32 *usrtt) { long m = mrtt; /* RTT */ - if (tcp_vegas_enabled(tp)) - vegas_rtt_calc(tp, mrtt); - /* The following amusing code comes from Jacobson's * article in SIGCOMM '88. Note that rtt and mdev * are scaled versions of rtt and mean deviation. @@ -670,7 +604,8 @@ static void tcp_rtt_estimator(struct tcp_sock *tp, __u32 mrtt) tp->rtt_seq = tp->snd_nxt; } - tcp_westwood_update_rtt(tp, tp->srtt >> 3); + if (tp->ca_ops->rtt_sample) + tp->ca_ops->rtt_sample(tp, *usrtt); } /* Calculate rto without backoff. This is the second half of Van Jacobson's @@ -1185,8 +1120,8 @@ void tcp_enter_frto(struct sock *sk) tp->snd_una == tp->high_seq || (tp->ca_state == TCP_CA_Loss && !tp->retransmits)) { tp->prior_ssthresh = tcp_current_ssthresh(tp); - if (!tcp_westwood_ssthresh(tp)) - tp->snd_ssthresh = tcp_recalc_ssthresh(tp); + tp->snd_ssthresh = tp->ca_ops->ssthresh(tp); + tcp_ca_event(tp, CA_EVENT_FRTO); } /* Have to clear retransmission markers here to keep the bookkeeping @@ -1252,8 +1187,6 @@ static void tcp_enter_frto_loss(struct sock *sk) tcp_set_ca_state(tp, TCP_CA_Loss); tp->high_seq = tp->frto_highmark; TCP_ECN_queue_cwr(tp); - - init_bictcp(tp); } void tcp_clear_retrans(struct tcp_sock *tp) @@ -1283,7 +1216,8 @@ void tcp_enter_loss(struct sock *sk, int how) if (tp->ca_state <= TCP_CA_Disorder || tp->snd_una == tp->high_seq || (tp->ca_state == TCP_CA_Loss && !tp->retransmits)) { tp->prior_ssthresh = tcp_current_ssthresh(tp); - tp->snd_ssthresh = tcp_recalc_ssthresh(tp); + tp->snd_ssthresh = tp->ca_ops->ssthresh(tp); + tcp_ca_event(tp, CA_EVENT_LOSS); } tp->snd_cwnd = 1; tp->snd_cwnd_cnt = 0; @@ -1596,28 +1530,14 @@ static inline void tcp_moderate_cwnd(struct tcp_sock *tp) } /* Decrease cwnd each second ack. */ - static void tcp_cwnd_down(struct tcp_sock *tp) { int decr = tp->snd_cwnd_cnt + 1; - __u32 limit; - - /* - * TCP Westwood - * Here limit is evaluated as BWestimation*RTTmin (for obtaining it - * in packets we use mss_cache). If sysctl_tcp_westwood is off - * tcp_westwood_bw_rttmin() returns 0. In such case snd_ssthresh is - * still used as usual. It prevents other strange cases in which - * BWE*RTTmin could assume value 0. It should not happen but... - */ - - if (!(limit = tcp_westwood_bw_rttmin(tp))) - limit = tp->snd_ssthresh/2; tp->snd_cwnd_cnt = decr&1; decr >>= 1; - if (decr && tp->snd_cwnd > limit) + if (decr && tp->snd_cwnd > tp->ca_ops->min_cwnd(tp)) tp->snd_cwnd -= decr; tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1); @@ -1654,8 +1574,8 @@ static void DBGUNDO(struct sock *sk, struct tcp_sock *tp, const char *msg) static void tcp_undo_cwr(struct tcp_sock *tp, int undo) { if (tp->prior_ssthresh) { - if (tcp_is_bic(tp)) - tp->snd_cwnd = max(tp->snd_cwnd, tp->bictcp.last_max_cwnd); + if (tp->ca_ops->undo_cwnd) + tp->snd_cwnd = tp->ca_ops->undo_cwnd(tp); else tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh<<1); @@ -1767,11 +1687,9 @@ static int tcp_try_undo_loss(struct sock *sk, struct tcp_sock *tp) static inline void tcp_complete_cwr(struct tcp_sock *tp) { - if (tcp_westwood_cwnd(tp)) - tp->snd_ssthresh = tp->snd_cwnd; - else - tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); + tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); tp->snd_cwnd_stamp = tcp_time_stamp; + tcp_ca_event(tp, CA_EVENT_COMPLETE_CWR); } static void tcp_try_to_open(struct sock *sk, struct tcp_sock *tp, int flag) @@ -1946,7 +1864,7 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una, if (tp->ca_state < TCP_CA_CWR) { if (!(flag&FLAG_ECE)) tp->prior_ssthresh = tcp_current_ssthresh(tp); - tp->snd_ssthresh = tcp_recalc_ssthresh(tp); + tp->snd_ssthresh = tp->ca_ops->ssthresh(tp); TCP_ECN_queue_cwr(tp); } @@ -1963,7 +1881,7 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una, /* Read draft-ietf-tcplw-high-performance before mucking * with this code. (Superceeds RFC1323) */ -static void tcp_ack_saw_tstamp(struct tcp_sock *tp, int flag) +static void tcp_ack_saw_tstamp(struct tcp_sock *tp, u32 *usrtt, int flag) { __u32 seq_rtt; @@ -1983,13 +1901,13 @@ static void tcp_ack_saw_tstamp(struct tcp_sock *tp, int flag) * in window is lost... Voila. --ANK (010210) */ seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr; - tcp_rtt_estimator(tp, seq_rtt); + tcp_rtt_estimator(tp, seq_rtt, usrtt); tcp_set_rto(tp); tp->backoff = 0; tcp_bound_rto(tp); } -static void tcp_ack_no_tstamp(struct tcp_sock *tp, u32 seq_rtt, int flag) +static void tcp_ack_no_tstamp(struct tcp_sock *tp, u32 seq_rtt, u32 *usrtt, int flag) { /* We don't have a timestamp. Can only use * packets that are not retransmitted to determine @@ -2003,338 +1921,29 @@ static void tcp_ack_no_tstamp(struct tcp_sock *tp, u32 seq_rtt, int flag) if (flag & FLAG_RETRANS_DATA_ACKED) return; - tcp_rtt_estimator(tp, seq_rtt); + tcp_rtt_estimator(tp, seq_rtt, usrtt); tcp_set_rto(tp); tp->backoff = 0; tcp_bound_rto(tp); } static inline void tcp_ack_update_rtt(struct tcp_sock *tp, - int flag, s32 seq_rtt) + int flag, s32 seq_rtt, u32 *usrtt) { /* Note that peer MAY send zero echo. In this case it is ignored. (rfc1323) */ if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) - tcp_ack_saw_tstamp(tp, flag); + tcp_ack_saw_tstamp(tp, usrtt, flag); else if (seq_rtt >= 0) - tcp_ack_no_tstamp(tp, seq_rtt, flag); + tcp_ack_no_tstamp(tp, seq_rtt, usrtt, flag); } -/* - * Compute congestion window to use. - * - * This is from the implementation of BICTCP in - * Lison-Xu, Kahaled Harfoush, and Injog Rhee. - * "Binary Increase Congestion Control for Fast, Long Distance - * Networks" in InfoComm 2004 - * Available from: - * http://www.csc.ncsu.edu/faculty/rhee/export/bitcp.pdf - * - * Unless BIC is enabled and congestion window is large - * this behaves the same as the original Reno. - */ -static inline __u32 bictcp_cwnd(struct tcp_sock *tp) +static inline void tcp_cong_avoid(struct tcp_sock *tp, u32 ack, u32 rtt, + u32 in_flight, int good) { - /* orignal Reno behaviour */ - if (!tcp_is_bic(tp)) - return tp->snd_cwnd; - - if (tp->bictcp.last_cwnd == tp->snd_cwnd && - (s32)(tcp_time_stamp - tp->bictcp.last_stamp) <= (HZ>>5)) - return tp->bictcp.cnt; - - tp->bictcp.last_cwnd = tp->snd_cwnd; - tp->bictcp.last_stamp = tcp_time_stamp; - - /* start off normal */ - if (tp->snd_cwnd <= sysctl_tcp_bic_low_window) - tp->bictcp.cnt = tp->snd_cwnd; - - /* binary increase */ - else if (tp->snd_cwnd < tp->bictcp.last_max_cwnd) { - __u32 dist = (tp->bictcp.last_max_cwnd - tp->snd_cwnd) - / BICTCP_B; - - if (dist > BICTCP_MAX_INCREMENT) - /* linear increase */ - tp->bictcp.cnt = tp->snd_cwnd / BICTCP_MAX_INCREMENT; - else if (dist <= 1U) - /* binary search increase */ - tp->bictcp.cnt = tp->snd_cwnd * BICTCP_FUNC_OF_MIN_INCR - / BICTCP_B; - else - /* binary search increase */ - tp->bictcp.cnt = tp->snd_cwnd / dist; - } else { - /* slow start amd linear increase */ - if (tp->snd_cwnd < tp->bictcp.last_max_cwnd + BICTCP_B) - /* slow start */ - tp->bictcp.cnt = tp->snd_cwnd * BICTCP_FUNC_OF_MIN_INCR - / BICTCP_B; - else if (tp->snd_cwnd < tp->bictcp.last_max_cwnd - + BICTCP_MAX_INCREMENT*(BICTCP_B-1)) - /* slow start */ - tp->bictcp.cnt = tp->snd_cwnd * (BICTCP_B-1) - / (tp->snd_cwnd-tp->bictcp.last_max_cwnd); - else - /* linear increase */ - tp->bictcp.cnt = tp->snd_cwnd / BICTCP_MAX_INCREMENT; - } - return tp->bictcp.cnt; -} - -/* This is Jacobson's slow start and congestion avoidance. - * SIGCOMM '88, p. 328. - */ -static inline void reno_cong_avoid(struct tcp_sock *tp) -{ - if (tp->snd_cwnd <= tp->snd_ssthresh) { - /* In "safe" area, increase. */ - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - } else { - /* In dangerous area, increase slowly. - * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd - */ - if (tp->snd_cwnd_cnt >= bictcp_cwnd(tp)) { - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - tp->snd_cwnd_cnt=0; - } else - tp->snd_cwnd_cnt++; - } + tp->ca_ops->cong_avoid(tp, ack, rtt, in_flight, good); tp->snd_cwnd_stamp = tcp_time_stamp; } -/* This is based on the congestion detection/avoidance scheme described in - * Lawrence S. Brakmo and Larry L. Peterson. - * "TCP Vegas: End to end congestion avoidance on a global internet." - * IEEE Journal on Selected Areas in Communication, 13(8):1465--1480, - * October 1995. Available from: - * ftp://ftp.cs.arizona.edu/xkernel/Papers/jsac.ps - * - * See http://www.cs.arizona.edu/xkernel/ for their implementation. - * The main aspects that distinguish this implementation from the - * Arizona Vegas implementation are: - * o We do not change the loss detection or recovery mechanisms of - * Linux in any way. Linux already recovers from losses quite well, - * using fine-grained timers, NewReno, and FACK. - * o To avoid the performance penalty imposed by increasing cwnd - * only every-other RTT during slow start, we increase during - * every RTT during slow start, just like Reno. - * o Largely to allow continuous cwnd growth during slow start, - * we use the rate at which ACKs come back as the "actual" - * rate, rather than the rate at which data is sent. - * o To speed convergence to the right rate, we set the cwnd - * to achieve the right ("actual") rate when we exit slow start. - * o To filter out the noise caused by delayed ACKs, we use the - * minimum RTT sample observed during the last RTT to calculate - * the actual rate. - * o When the sender re-starts from idle, it waits until it has - * received ACKs for an entire flight of new data before making - * a cwnd adjustment decision. The original Vegas implementation - * assumed senders never went idle. - */ -static void vegas_cong_avoid(struct tcp_sock *tp, u32 ack, u32 seq_rtt) -{ - /* The key players are v_beg_snd_una and v_beg_snd_nxt. - * - * These are so named because they represent the approximate values - * of snd_una and snd_nxt at the beginning of the current RTT. More - * precisely, they represent the amount of data sent during the RTT. - * At the end of the RTT, when we receive an ACK for v_beg_snd_nxt, - * we will calculate that (v_beg_snd_nxt - v_beg_snd_una) outstanding - * bytes of data have been ACKed during the course of the RTT, giving - * an "actual" rate of: - * - * (v_beg_snd_nxt - v_beg_snd_una) / (rtt duration) - * - * Unfortunately, v_beg_snd_una is not exactly equal to snd_una, - * because delayed ACKs can cover more than one segment, so they - * don't line up nicely with the boundaries of RTTs. - * - * Another unfortunate fact of life is that delayed ACKs delay the - * advance of the left edge of our send window, so that the number - * of bytes we send in an RTT is often less than our cwnd will allow. - * So we keep track of our cwnd separately, in v_beg_snd_cwnd. - */ - - if (after(ack, tp->vegas.beg_snd_nxt)) { - /* Do the Vegas once-per-RTT cwnd adjustment. */ - u32 old_wnd, old_snd_cwnd; - - - /* Here old_wnd is essentially the window of data that was - * sent during the previous RTT, and has all - * been acknowledged in the course of the RTT that ended - * with the ACK we just received. Likewise, old_snd_cwnd - * is the cwnd during the previous RTT. - */ - old_wnd = (tp->vegas.beg_snd_nxt - tp->vegas.beg_snd_una) / - tp->mss_cache_std; - old_snd_cwnd = tp->vegas.beg_snd_cwnd; - - /* Save the extent of the current window so we can use this - * at the end of the next RTT. - */ - tp->vegas.beg_snd_una = tp->vegas.beg_snd_nxt; - tp->vegas.beg_snd_nxt = tp->snd_nxt; - tp->vegas.beg_snd_cwnd = tp->snd_cwnd; - - /* Take into account the current RTT sample too, to - * decrease the impact of delayed acks. This double counts - * this sample since we count it for the next window as well, - * but that's not too awful, since we're taking the min, - * rather than averaging. - */ - vegas_rtt_calc(tp, seq_rtt); - - /* We do the Vegas calculations only if we got enough RTT - * samples that we can be reasonably sure that we got - * at least one RTT sample that wasn't from a delayed ACK. - * If we only had 2 samples total, - * then that means we're getting only 1 ACK per RTT, which - * means they're almost certainly delayed ACKs. - * If we have 3 samples, we should be OK. - */ - - if (tp->vegas.cntRTT <= 2) { - /* We don't have enough RTT samples to do the Vegas - * calculation, so we'll behave like Reno. - */ - if (tp->snd_cwnd > tp->snd_ssthresh) - tp->snd_cwnd++; - } else { - u32 rtt, target_cwnd, diff; - - /* We have enough RTT samples, so, using the Vegas - * algorithm, we determine if we should increase or - * decrease cwnd, and by how much. - */ - - /* Pluck out the RTT we are using for the Vegas - * calculations. This is the min RTT seen during the - * last RTT. Taking the min filters out the effects - * of delayed ACKs, at the cost of noticing congestion - * a bit later. - */ - rtt = tp->vegas.minRTT; - - /* Calculate the cwnd we should have, if we weren't - * going too fast. - * - * This is: - * (actual rate in segments) * baseRTT - * We keep it as a fixed point number with - * V_PARAM_SHIFT bits to the right of the binary point. - */ - target_cwnd = ((old_wnd * tp->vegas.baseRTT) - << V_PARAM_SHIFT) / rtt; - - /* Calculate the difference between the window we had, - * and the window we would like to have. This quantity - * is the "Diff" from the Arizona Vegas papers. - * - * Again, this is a fixed point number with - * V_PARAM_SHIFT bits to the right of the binary - * point. - */ - diff = (old_wnd << V_PARAM_SHIFT) - target_cwnd; - - if (tp->snd_cwnd < tp->snd_ssthresh) { - /* Slow start. */ - if (diff > sysctl_tcp_vegas_gamma) { - /* Going too fast. Time to slow down - * and switch to congestion avoidance. - */ - tp->snd_ssthresh = 2; - - /* Set cwnd to match the actual rate - * exactly: - * cwnd = (actual rate) * baseRTT - * Then we add 1 because the integer - * truncation robs us of full link - * utilization. - */ - tp->snd_cwnd = min(tp->snd_cwnd, - (target_cwnd >> - V_PARAM_SHIFT)+1); - - } - } else { - /* Congestion avoidance. */ - u32 next_snd_cwnd; - - /* Figure out where we would like cwnd - * to be. - */ - if (diff > sysctl_tcp_vegas_beta) { - /* The old window was too fast, so - * we slow down. - */ - next_snd_cwnd = old_snd_cwnd - 1; - } else if (diff < sysctl_tcp_vegas_alpha) { - /* We don't have enough extra packets - * in the network, so speed up. - */ - next_snd_cwnd = old_snd_cwnd + 1; - } else { - /* Sending just as fast as we - * should be. - */ - next_snd_cwnd = old_snd_cwnd; - } - - /* Adjust cwnd upward or downward, toward the - * desired value. - */ - if (next_snd_cwnd > tp->snd_cwnd) - tp->snd_cwnd++; - else if (next_snd_cwnd < tp->snd_cwnd) - tp->snd_cwnd--; - } - } - - /* Wipe the slate clean for the next RTT. */ - tp->vegas.cntRTT = 0; - tp->vegas.minRTT = 0x7fffffff; - } - - /* The following code is executed for every ack we receive, - * except for conditions checked in should_advance_cwnd() - * before the call to tcp_cong_avoid(). Mainly this means that - * we only execute this code if the ack actually acked some - * data. - */ - - /* If we are in slow start, increase our cwnd in response to this ACK. - * (If we are not in slow start then we are in congestion avoidance, - * and adjust our congestion window only once per RTT. See the code - * above.) - */ - if (tp->snd_cwnd <= tp->snd_ssthresh) - tp->snd_cwnd++; - - /* to keep cwnd from growing without bound */ - tp->snd_cwnd = min_t(u32, tp->snd_cwnd, tp->snd_cwnd_clamp); - - /* Make sure that we are never so timid as to reduce our cwnd below - * 2 MSS. - * - * Going below 2 MSS would risk huge delayed ACKs from our receiver. - */ - tp->snd_cwnd = max(tp->snd_cwnd, 2U); - - tp->snd_cwnd_stamp = tcp_time_stamp; -} - -static inline void tcp_cong_avoid(struct tcp_sock *tp, u32 ack, u32 seq_rtt) -{ - if (tcp_vegas_enabled(tp)) - vegas_cong_avoid(tp, ack, seq_rtt); - else - reno_cong_avoid(tp); -} - /* Restart timer after forward progress on connection. * RFC2988 recommends to restart timer to now+rto. */ @@ -2415,13 +2024,18 @@ static int tcp_tso_acked(struct sock *sk, struct sk_buff *skb, /* Remove acknowledged frames from the retransmission queue. */ -static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p) +static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p, s32 *seq_usrtt) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; __u32 now = tcp_time_stamp; int acked = 0; __s32 seq_rtt = -1; + struct timeval usnow; + u32 pkts_acked = 0; + + if (seq_usrtt) + do_gettimeofday(&usnow); while ((skb = skb_peek(&sk->sk_write_queue)) && skb != sk->sk_send_head) { @@ -2448,6 +2062,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p) */ if (!(scb->flags & TCPCB_FLAG_SYN)) { acked |= FLAG_DATA_ACKED; + ++pkts_acked; } else { acked |= FLAG_SYN_ACKED; tp->retrans_stamp = 0; @@ -2461,6 +2076,10 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p) seq_rtt = -1; } else if (seq_rtt < 0) seq_rtt = now - scb->when; + if (seq_usrtt) + *seq_usrtt = (usnow.tv_sec - skb->stamp.tv_sec) * 1000000 + + (usnow.tv_usec - skb->stamp.tv_usec); + if (sacked & TCPCB_SACKED_ACKED) tp->sacked_out -= tcp_skb_pcount(skb); if (sacked & TCPCB_LOST) @@ -2479,8 +2098,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p) } if (acked&FLAG_ACKED) { - tcp_ack_update_rtt(tp, acked, seq_rtt); + tcp_ack_update_rtt(tp, acked, seq_rtt, seq_usrtt); tcp_ack_packets_out(sk, tp); + + if (tp->ca_ops->pkts_acked) + tp->ca_ops->pkts_acked(tp, pkts_acked); } #if FASTRETRANS_DEBUG > 0 @@ -2624,257 +2246,6 @@ static void tcp_process_frto(struct sock *sk, u32 prior_snd_una) tp->frto_counter = (tp->frto_counter + 1) % 3; } -/* - * TCP Westwood+ - */ - -/* - * @init_westwood - * This function initializes fields used in TCP Westwood+. We can't - * get no information about RTTmin at this time so we simply set it to - * TCP_WESTWOOD_INIT_RTT. This value was chosen to be too conservative - * since in this way we're sure it will be updated in a consistent - * way as soon as possible. It will reasonably happen within the first - * RTT period of the connection lifetime. - */ - -static void init_westwood(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tp->westwood.bw_ns_est = 0; - tp->westwood.bw_est = 0; - tp->westwood.accounted = 0; - tp->westwood.cumul_ack = 0; - tp->westwood.rtt_win_sx = tcp_time_stamp; - tp->westwood.rtt = TCP_WESTWOOD_INIT_RTT; - tp->westwood.rtt_min = TCP_WESTWOOD_INIT_RTT; - tp->westwood.snd_una = tp->snd_una; -} - -/* - * @westwood_do_filter - * Low-pass filter. Implemented using constant coeffients. - */ - -static inline __u32 westwood_do_filter(__u32 a, __u32 b) -{ - return (((7 * a) + b) >> 3); -} - -static void westwood_filter(struct sock *sk, __u32 delta) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tp->westwood.bw_ns_est = - westwood_do_filter(tp->westwood.bw_ns_est, - tp->westwood.bk / delta); - tp->westwood.bw_est = - westwood_do_filter(tp->westwood.bw_est, - tp->westwood.bw_ns_est); -} - -/* - * @westwood_update_rttmin - * It is used to update RTTmin. In this case we MUST NOT use - * WESTWOOD_RTT_MIN minimum bound since we could be on a LAN! - */ - -static inline __u32 westwood_update_rttmin(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - __u32 rttmin = tp->westwood.rtt_min; - - if (tp->westwood.rtt != 0 && - (tp->westwood.rtt < tp->westwood.rtt_min || !rttmin)) - rttmin = tp->westwood.rtt; - - return rttmin; -} - -/* - * @westwood_acked - * Evaluate increases for dk. - */ - -static inline __u32 westwood_acked(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - - return tp->snd_una - tp->westwood.snd_una; -} - -/* - * @westwood_new_window - * It evaluates if we are receiving data inside the same RTT window as - * when we started. - * Return value: - * It returns 0 if we are still evaluating samples in the same RTT - * window, 1 if the sample has to be considered in the next window. - */ - -static int westwood_new_window(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - __u32 left_bound; - __u32 rtt; - int ret = 0; - - left_bound = tp->westwood.rtt_win_sx; - rtt = max(tp->westwood.rtt, (u32) TCP_WESTWOOD_RTT_MIN); - - /* - * A RTT-window has passed. Be careful since if RTT is less than - * 50ms we don't filter but we continue 'building the sample'. - * This minimum limit was choosen since an estimation on small - * time intervals is better to avoid... - * Obvioulsy on a LAN we reasonably will always have - * right_bound = left_bound + WESTWOOD_RTT_MIN - */ - - if ((left_bound + rtt) < tcp_time_stamp) - ret = 1; - - return ret; -} - -/* - * @westwood_update_window - * It updates RTT evaluation window if it is the right moment to do - * it. If so it calls filter for evaluating bandwidth. - */ - -static void __westwood_update_window(struct sock *sk, __u32 now) -{ - struct tcp_sock *tp = tcp_sk(sk); - __u32 delta = now - tp->westwood.rtt_win_sx; - - if (delta) { - if (tp->westwood.rtt) - westwood_filter(sk, delta); - - tp->westwood.bk = 0; - tp->westwood.rtt_win_sx = tcp_time_stamp; - } -} - - -static void westwood_update_window(struct sock *sk, __u32 now) -{ - if (westwood_new_window(sk)) - __westwood_update_window(sk, now); -} - -/* - * @__tcp_westwood_fast_bw - * It is called when we are in fast path. In particular it is called when - * header prediction is successfull. In such case infact update is - * straight forward and doesn't need any particular care. - */ - -static void __tcp_westwood_fast_bw(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_sock *tp = tcp_sk(sk); - - westwood_update_window(sk, tcp_time_stamp); - - tp->westwood.bk += westwood_acked(sk); - tp->westwood.snd_una = tp->snd_una; - tp->westwood.rtt_min = westwood_update_rttmin(sk); -} - -static inline void tcp_westwood_fast_bw(struct sock *sk, struct sk_buff *skb) -{ - if (tcp_is_westwood(tcp_sk(sk))) - __tcp_westwood_fast_bw(sk, skb); -} - - -/* - * @westwood_dupack_update - * It updates accounted and cumul_ack when receiving a dupack. - */ - -static void westwood_dupack_update(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tp->westwood.accounted += tp->mss_cache_std; - tp->westwood.cumul_ack = tp->mss_cache_std; -} - -static inline int westwood_may_change_cumul(struct tcp_sock *tp) -{ - return (tp->westwood.cumul_ack > tp->mss_cache_std); -} - -static inline void westwood_partial_update(struct tcp_sock *tp) -{ - tp->westwood.accounted -= tp->westwood.cumul_ack; - tp->westwood.cumul_ack = tp->mss_cache_std; -} - -static inline void westwood_complete_update(struct tcp_sock *tp) -{ - tp->westwood.cumul_ack -= tp->westwood.accounted; - tp->westwood.accounted = 0; -} - -/* - * @westwood_acked_count - * This function evaluates cumul_ack for evaluating dk in case of - * delayed or partial acks. - */ - -static inline __u32 westwood_acked_count(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tp->westwood.cumul_ack = westwood_acked(sk); - - /* If cumul_ack is 0 this is a dupack since it's not moving - * tp->snd_una. - */ - if (!(tp->westwood.cumul_ack)) - westwood_dupack_update(sk); - - if (westwood_may_change_cumul(tp)) { - /* Partial or delayed ack */ - if (tp->westwood.accounted >= tp->westwood.cumul_ack) - westwood_partial_update(tp); - else - westwood_complete_update(tp); - } - - tp->westwood.snd_una = tp->snd_una; - - return tp->westwood.cumul_ack; -} - - -/* - * @__tcp_westwood_slow_bw - * It is called when something is going wrong..even if there could - * be no problems! Infact a simple delayed packet may trigger a - * dupack. But we need to be careful in such case. - */ - -static void __tcp_westwood_slow_bw(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_sock *tp = tcp_sk(sk); - - westwood_update_window(sk, tcp_time_stamp); - - tp->westwood.bk += westwood_acked_count(sk); - tp->westwood.rtt_min = westwood_update_rttmin(sk); -} - -static inline void tcp_westwood_slow_bw(struct sock *sk, struct sk_buff *skb) -{ - if (tcp_is_westwood(tcp_sk(sk))) - __tcp_westwood_slow_bw(sk, skb); -} - /* This routine deals with incoming acks, but not outgoing ones. */ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) { @@ -2884,6 +2255,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) u32 ack = TCP_SKB_CB(skb)->ack_seq; u32 prior_in_flight; s32 seq_rtt; + s32 seq_usrtt = 0; int prior_packets; /* If the ack is newer than sent or older than previous acks @@ -2902,9 +2274,10 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) */ tcp_update_wl(tp, ack, ack_seq); tp->snd_una = ack; - tcp_westwood_fast_bw(sk, skb); flag |= FLAG_WIN_UPDATE; + tcp_ca_event(tp, CA_EVENT_FAST_ACK); + NET_INC_STATS_BH(LINUX_MIB_TCPHPACKS); } else { if (ack_seq != TCP_SKB_CB(skb)->end_seq) @@ -2920,7 +2293,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) if (TCP_ECN_rcv_ecn_echo(tp, skb->h.th)) flag |= FLAG_ECE; - tcp_westwood_slow_bw(sk,skb); + tcp_ca_event(tp, CA_EVENT_SLOW_ACK); } /* We passed data and got it acked, remove any soft error @@ -2935,22 +2308,20 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) prior_in_flight = tcp_packets_in_flight(tp); /* See if we can take anything off of the retransmit queue. */ - flag |= tcp_clean_rtx_queue(sk, &seq_rtt); + flag |= tcp_clean_rtx_queue(sk, &seq_rtt, + tp->ca_ops->rtt_sample ? &seq_usrtt : NULL); if (tp->frto_counter) tcp_process_frto(sk, prior_snd_una); if (tcp_ack_is_dubious(tp, flag)) { /* Advanve CWND, if state allows this. */ - if ((flag & FLAG_DATA_ACKED) && - (tcp_vegas_enabled(tp) || prior_in_flight >= tp->snd_cwnd) && - tcp_may_raise_cwnd(tp, flag)) - tcp_cong_avoid(tp, ack, seq_rtt); + if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(tp, flag)) + tcp_cong_avoid(tp, ack, seq_rtt, prior_in_flight, 0); tcp_fastretrans_alert(sk, prior_snd_una, prior_packets, flag); } else { - if ((flag & FLAG_DATA_ACKED) && - (tcp_vegas_enabled(tp) || prior_in_flight >= tp->snd_cwnd)) - tcp_cong_avoid(tp, ack, seq_rtt); + if ((flag & FLAG_DATA_ACKED)) + tcp_cong_avoid(tp, ack, seq_rtt, prior_in_flight, 1); } if ((flag & FLAG_FORWARD_PROGRESS) || !(flag&FLAG_NOT_DUP)) @@ -4552,6 +3923,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tcp_init_metrics(sk); + tcp_init_congestion_control(tp); + /* Prevent spurious tcp_cwnd_restart() on first data * packet. */ @@ -4708,9 +4081,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if(tp->af_specific->conn_request(sk, skb) < 0) return 1; - init_westwood(sk); - init_bictcp(tp); - /* Now we have several options: In theory there is * nothing else in the frame. KA9Q has an option to * send data with the syn, BSD accepts data with the @@ -4732,9 +4102,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, goto discard; case TCP_SYN_SENT: - init_westwood(sk); - init_bictcp(tp); - queued = tcp_rcv_synsent_state_process(sk, skb, th, len); if (queued >= 0) return queued; @@ -4816,7 +4183,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, */ if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr && !tp->srtt) - tcp_ack_saw_tstamp(tp, 0); + tcp_ack_saw_tstamp(tp, 0, 0); if (tp->rx_opt.tstamp_ok) tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; @@ -4828,6 +4195,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tcp_init_metrics(sk); + tcp_init_congestion_control(tp); + /* Prevent spurious tcp_cwnd_restart() on * first data packet. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2d41d5d6ad19..ebf112347a97 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2048,6 +2048,7 @@ static int tcp_v4_init_sock(struct sock *sk) tp->mss_cache_std = tp->mss_cache = 536; tp->reordering = sysctl_tcp_reordering; + tp->ca_ops = &tcp_init_congestion_ops; sk->sk_state = TCP_CLOSE; @@ -2070,6 +2071,8 @@ int tcp_v4_destroy_sock(struct sock *sk) tcp_clear_xmit_timers(sk); + tcp_cleanup_congestion_control(tp); + /* Cleanup up the write buffer. */ sk_stream_writequeue_purge(sk); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index b3943e7562f3..f42a284164b7 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -774,6 +774,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->frto_counter = 0; newtp->frto_highmark = 0; + newtp->ca_ops = &tcp_reno; + tcp_set_ca_state(newtp, TCP_CA_Open); tcp_init_xmit_timers(newsk); skb_queue_head_init(&newtp->out_of_order_queue); @@ -842,8 +844,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, if (newtp->ecn_flags&TCP_ECN_OK) sock_set_flag(newsk, SOCK_NO_LARGESEND); - tcp_ca_init(newtp); - TCP_INC_STATS_BH(TCP_MIB_PASSIVEOPENS); } return newsk; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f17c6577e337..0e17c244875c 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -111,8 +111,7 @@ static void tcp_cwnd_restart(struct tcp_sock *tp, struct dst_entry *dst) u32 restart_cwnd = tcp_init_cwnd(tp, dst); u32 cwnd = tp->snd_cwnd; - if (tcp_is_vegas(tp)) - tcp_vegas_enable(tp); + tcp_ca_event(tp, CA_EVENT_CWND_RESTART); tp->snd_ssthresh = tcp_current_ssthresh(tp); restart_cwnd = min(restart_cwnd, cwnd); @@ -280,6 +279,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb) #define SYSCTL_FLAG_WSCALE 0x2 #define SYSCTL_FLAG_SACK 0x4 + /* If congestion control is doing timestamping */ + if (tp->ca_ops->rtt_sample) + do_gettimeofday(&skb->stamp); + sysctl_flags = 0; if (tcb->flags & TCPCB_FLAG_SYN) { tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS; @@ -304,17 +307,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb) (tp->rx_opt.eff_sacks * TCPOLEN_SACK_PERBLOCK)); } - /* - * If the connection is idle and we are restarting, - * then we don't want to do any Vegas calculations - * until we get fresh RTT samples. So when we - * restart, we reset our Vegas state to a clean - * slate. After we get acks for this flight of - * packets, _then_ we can make Vegas calculations - * again. - */ - if (tcp_is_vegas(tp) && tcp_packets_in_flight(tp) == 0) - tcp_vegas_enable(tp); + if (tcp_packets_in_flight(tp) == 0) + tcp_ca_event(tp, CA_EVENT_TX_START); th = (struct tcphdr *) skb_push(skb, tcp_header_size); skb->h.th = th; @@ -521,6 +515,7 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) * skbs, which it never sent before. --ANK */ TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when; + buff->stamp = skb->stamp; if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST) { tp->lost_out -= tcp_skb_pcount(skb); @@ -1449,7 +1444,6 @@ static inline void tcp_connect_init(struct sock *sk) tp->window_clamp = dst_metric(dst, RTAX_WINDOW); tp->advmss = dst_metric(dst, RTAX_ADVMSS); tcp_initialize_rcv_mss(sk); - tcp_ca_init(tp); tcp_select_initial_window(tcp_full_space(sk), tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), @@ -1503,7 +1497,6 @@ int tcp_connect(struct sock *sk) TCP_SKB_CB(buff)->end_seq = tp->write_seq; tp->snd_nxt = tp->write_seq; tp->pushed_seq = tp->write_seq; - tcp_ca_init(tp); /* Send it off. */ TCP_SKB_CB(buff)->when = tcp_time_stamp; diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c new file mode 100644 index 000000000000..70e108e15c71 --- /dev/null +++ b/net/ipv4/tcp_scalable.c @@ -0,0 +1,68 @@ +/* Tom Kelly's Scalable TCP + * + * See htt://www-lce.eng.cam.ac.uk/~ctk21/scalable/ + * + * John Heffner + */ + +#include +#include +#include + +/* These factors derived from the recommended values in the aer: + * .01 and and 7/8. We use 50 instead of 100 to account for + * delayed ack. + */ +#define TCP_SCALABLE_AI_CNT 50U +#define TCP_SCALABLE_MD_SCALE 3 + +static void tcp_scalable_cong_avoid(struct tcp_sock *tp, u32 ack, u32 rtt, + u32 in_flight, int flag) +{ + if (in_flight < tp->snd_cwnd) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + tp->snd_cwnd++; + } else { + tp->snd_cwnd_cnt++; + if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)){ + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } + } + tp->snd_cwnd = min_t(u32, tp->snd_cwnd, tp->snd_cwnd_clamp); + tp->snd_cwnd_stamp = tcp_time_stamp; +} + +static u32 tcp_scalable_ssthresh(struct tcp_sock *tp) +{ + return max(tp->snd_cwnd - (tp->snd_cwnd>>TCP_SCALABLE_MD_SCALE), 2U); +} + + +static struct tcp_congestion_ops tcp_scalable = { + .ssthresh = tcp_scalable_ssthresh, + .cong_avoid = tcp_scalable_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, + + .owner = THIS_MODULE, + .name = "scalable", +}; + +static int __init tcp_scalable_register(void) +{ + return tcp_register_congestion_control(&tcp_scalable); +} + +static void __exit tcp_scalable_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_scalable); +} + +module_init(tcp_scalable_register); +module_exit(tcp_scalable_unregister); + +MODULE_AUTHOR("John Heffner"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Scalable TCP"); diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c new file mode 100644 index 000000000000..9bd443db5193 --- /dev/null +++ b/net/ipv4/tcp_vegas.c @@ -0,0 +1,411 @@ +/* + * TCP Vegas congestion control + * + * This is based on the congestion detection/avoidance scheme described in + * Lawrence S. Brakmo and Larry L. Peterson. + * "TCP Vegas: End to end congestion avoidance on a global internet." + * IEEE Journal on Selected Areas in Communication, 13(8):1465--1480, + * October 1995. Available from: + * ftp://ftp.cs.arizona.edu/xkernel/Papers/jsac.ps + * + * See http://www.cs.arizona.edu/xkernel/ for their implementation. + * The main aspects that distinguish this implementation from the + * Arizona Vegas implementation are: + * o We do not change the loss detection or recovery mechanisms of + * Linux in any way. Linux already recovers from losses quite well, + * using fine-grained timers, NewReno, and FACK. + * o To avoid the performance penalty imposed by increasing cwnd + * only every-other RTT during slow start, we increase during + * every RTT during slow start, just like Reno. + * o Largely to allow continuous cwnd growth during slow start, + * we use the rate at which ACKs come back as the "actual" + * rate, rather than the rate at which data is sent. + * o To speed convergence to the right rate, we set the cwnd + * to achieve the right ("actual") rate when we exit slow start. + * o To filter out the noise caused by delayed ACKs, we use the + * minimum RTT sample observed during the last RTT to calculate + * the actual rate. + * o When the sender re-starts from idle, it waits until it has + * received ACKs for an entire flight of new data before making + * a cwnd adjustment decision. The original Vegas implementation + * assumed senders never went idle. + */ + +#include +#include +#include +#include +#include + +#include + +/* Default values of the Vegas variables, in fixed-point representation + * with V_PARAM_SHIFT bits to the right of the binary point. + */ +#define V_PARAM_SHIFT 1 +static int alpha = 1<doing_vegas_now = 1; + + /* Set the beginning of the next send window. */ + vegas->beg_snd_nxt = tp->snd_nxt; + + vegas->cntRTT = 0; + vegas->minRTT = 0x7fffffff; +} + +/* Stop taking Vegas samples for now. */ +static inline void vegas_disable(struct tcp_sock *tp) +{ + struct vegas *vegas = tcp_ca(tp); + + vegas->doing_vegas_now = 0; +} + +static void tcp_vegas_init(struct tcp_sock *tp) +{ + struct vegas *vegas = tcp_ca(tp); + + vegas->baseRTT = 0x7fffffff; + vegas_enable(tp); +} + +/* Do RTT sampling needed for Vegas. + * Basically we: + * o min-filter RTT samples from within an RTT to get the current + * propagation delay + queuing delay (we are min-filtering to try to + * avoid the effects of delayed ACKs) + * o min-filter RTT samples from a much longer window (forever for now) + * to find the propagation delay (baseRTT) + */ +static void tcp_vegas_rtt_calc(struct tcp_sock *tp, u32 usrtt) +{ + struct vegas *vegas = tcp_ca(tp); + u32 vrtt = usrtt + 1; /* Never allow zero rtt or baseRTT */ + + /* Filter to find propagation delay: */ + if (vrtt < vegas->baseRTT) + vegas->baseRTT = vrtt; + + /* Find the min RTT during the last RTT to find + * the current prop. delay + queuing delay: + */ + vegas->minRTT = min(vegas->minRTT, vrtt); + vegas->cntRTT++; +} + +static void tcp_vegas_state(struct tcp_sock *tp, u8 ca_state) +{ + + if (ca_state == TCP_CA_Open) + vegas_enable(tp); + else + vegas_disable(tp); +} + +/* + * If the connection is idle and we are restarting, + * then we don't want to do any Vegas calculations + * until we get fresh RTT samples. So when we + * restart, we reset our Vegas state to a clean + * slate. After we get acks for this flight of + * packets, _then_ we can make Vegas calculations + * again. + */ +static void tcp_vegas_cwnd_event(struct tcp_sock *tp, enum tcp_ca_event event) +{ + if (event == CA_EVENT_CWND_RESTART || + event == CA_EVENT_TX_START) + tcp_vegas_init(tp); +} + +static void tcp_vegas_cong_avoid(struct tcp_sock *tp, u32 ack, + u32 seq_rtt, u32 in_flight, int flag) +{ + struct vegas *vegas = tcp_ca(tp); + + if (!vegas->doing_vegas_now) + return tcp_reno_cong_avoid(tp, ack, seq_rtt, in_flight, flag); + + /* The key players are v_beg_snd_una and v_beg_snd_nxt. + * + * These are so named because they represent the approximate values + * of snd_una and snd_nxt at the beginning of the current RTT. More + * precisely, they represent the amount of data sent during the RTT. + * At the end of the RTT, when we receive an ACK for v_beg_snd_nxt, + * we will calculate that (v_beg_snd_nxt - v_beg_snd_una) outstanding + * bytes of data have been ACKed during the course of the RTT, giving + * an "actual" rate of: + * + * (v_beg_snd_nxt - v_beg_snd_una) / (rtt duration) + * + * Unfortunately, v_beg_snd_una is not exactly equal to snd_una, + * because delayed ACKs can cover more than one segment, so they + * don't line up nicely with the boundaries of RTTs. + * + * Another unfortunate fact of life is that delayed ACKs delay the + * advance of the left edge of our send window, so that the number + * of bytes we send in an RTT is often less than our cwnd will allow. + * So we keep track of our cwnd separately, in v_beg_snd_cwnd. + */ + + if (after(ack, vegas->beg_snd_nxt)) { + /* Do the Vegas once-per-RTT cwnd adjustment. */ + u32 old_wnd, old_snd_cwnd; + + + /* Here old_wnd is essentially the window of data that was + * sent during the previous RTT, and has all + * been acknowledged in the course of the RTT that ended + * with the ACK we just received. Likewise, old_snd_cwnd + * is the cwnd during the previous RTT. + */ + old_wnd = (vegas->beg_snd_nxt - vegas->beg_snd_una) / + tp->mss_cache; + old_snd_cwnd = vegas->beg_snd_cwnd; + + /* Save the extent of the current window so we can use this + * at the end of the next RTT. + */ + vegas->beg_snd_una = vegas->beg_snd_nxt; + vegas->beg_snd_nxt = tp->snd_nxt; + vegas->beg_snd_cwnd = tp->snd_cwnd; + + /* Take into account the current RTT sample too, to + * decrease the impact of delayed acks. This double counts + * this sample since we count it for the next window as well, + * but that's not too awful, since we're taking the min, + * rather than averaging. + */ + tcp_vegas_rtt_calc(tp, seq_rtt*1000); + + /* We do the Vegas calculations only if we got enough RTT + * samples that we can be reasonably sure that we got + * at least one RTT sample that wasn't from a delayed ACK. + * If we only had 2 samples total, + * then that means we're getting only 1 ACK per RTT, which + * means they're almost certainly delayed ACKs. + * If we have 3 samples, we should be OK. + */ + + if (vegas->cntRTT <= 2) { + /* We don't have enough RTT samples to do the Vegas + * calculation, so we'll behave like Reno. + */ + if (tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd++; + } else { + u32 rtt, target_cwnd, diff; + + /* We have enough RTT samples, so, using the Vegas + * algorithm, we determine if we should increase or + * decrease cwnd, and by how much. + */ + + /* Pluck out the RTT we are using for the Vegas + * calculations. This is the min RTT seen during the + * last RTT. Taking the min filters out the effects + * of delayed ACKs, at the cost of noticing congestion + * a bit later. + */ + rtt = vegas->minRTT; + + /* Calculate the cwnd we should have, if we weren't + * going too fast. + * + * This is: + * (actual rate in segments) * baseRTT + * We keep it as a fixed point number with + * V_PARAM_SHIFT bits to the right of the binary point. + */ + target_cwnd = ((old_wnd * vegas->baseRTT) + << V_PARAM_SHIFT) / rtt; + + /* Calculate the difference between the window we had, + * and the window we would like to have. This quantity + * is the "Diff" from the Arizona Vegas papers. + * + * Again, this is a fixed point number with + * V_PARAM_SHIFT bits to the right of the binary + * point. + */ + diff = (old_wnd << V_PARAM_SHIFT) - target_cwnd; + + if (tp->snd_cwnd < tp->snd_ssthresh) { + /* Slow start. */ + if (diff > gamma) { + /* Going too fast. Time to slow down + * and switch to congestion avoidance. + */ + tp->snd_ssthresh = 2; + + /* Set cwnd to match the actual rate + * exactly: + * cwnd = (actual rate) * baseRTT + * Then we add 1 because the integer + * truncation robs us of full link + * utilization. + */ + tp->snd_cwnd = min(tp->snd_cwnd, + (target_cwnd >> + V_PARAM_SHIFT)+1); + + } + } else { + /* Congestion avoidance. */ + u32 next_snd_cwnd; + + /* Figure out where we would like cwnd + * to be. + */ + if (diff > beta) { + /* The old window was too fast, so + * we slow down. + */ + next_snd_cwnd = old_snd_cwnd - 1; + } else if (diff < alpha) { + /* We don't have enough extra packets + * in the network, so speed up. + */ + next_snd_cwnd = old_snd_cwnd + 1; + } else { + /* Sending just as fast as we + * should be. + */ + next_snd_cwnd = old_snd_cwnd; + } + + /* Adjust cwnd upward or downward, toward the + * desired value. + */ + if (next_snd_cwnd > tp->snd_cwnd) + tp->snd_cwnd++; + else if (next_snd_cwnd < tp->snd_cwnd) + tp->snd_cwnd--; + } + } + + /* Wipe the slate clean for the next RTT. */ + vegas->cntRTT = 0; + vegas->minRTT = 0x7fffffff; + } + + /* The following code is executed for every ack we receive, + * except for conditions checked in should_advance_cwnd() + * before the call to tcp_cong_avoid(). Mainly this means that + * we only execute this code if the ack actually acked some + * data. + */ + + /* If we are in slow start, increase our cwnd in response to this ACK. + * (If we are not in slow start then we are in congestion avoidance, + * and adjust our congestion window only once per RTT. See the code + * above.) + */ + if (tp->snd_cwnd <= tp->snd_ssthresh) + tp->snd_cwnd++; + + /* to keep cwnd from growing without bound */ + tp->snd_cwnd = min_t(u32, tp->snd_cwnd, tp->snd_cwnd_clamp); + + /* Make sure that we are never so timid as to reduce our cwnd below + * 2 MSS. + * + * Going below 2 MSS would risk huge delayed ACKs from our receiver. + */ + tp->snd_cwnd = max(tp->snd_cwnd, 2U); +} + +/* Extract info for Tcp socket info provided via netlink. */ +static void tcp_vegas_get_info(struct tcp_sock *tp, u32 ext, + struct sk_buff *skb) +{ + const struct vegas *ca = tcp_ca(tp); + if (ext & (1<<(TCPDIAG_VEGASINFO-1))) { + struct tcpvegas_info *info; + + info = RTA_DATA(__RTA_PUT(skb, TCPDIAG_VEGASINFO, + sizeof(*info))); + + info->tcpv_enabled = ca->doing_vegas_now; + info->tcpv_rttcnt = ca->cntRTT; + info->tcpv_rtt = ca->baseRTT; + info->tcpv_minrtt = ca->minRTT; + rtattr_failure: ; + } +} + +static struct tcp_congestion_ops tcp_vegas = { + .init = tcp_vegas_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = tcp_vegas_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, + .rtt_sample = tcp_vegas_rtt_calc, + .set_state = tcp_vegas_state, + .cwnd_event = tcp_vegas_cwnd_event, + .get_info = tcp_vegas_get_info, + + .owner = THIS_MODULE, + .name = "vegas", +}; + +static int __init tcp_vegas_register(void) +{ + BUG_ON(sizeof(struct vegas) > TCP_CA_PRIV_SIZE); + tcp_register_congestion_control(&tcp_vegas); + return 0; +} + +static void __exit tcp_vegas_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_vegas); +} + +module_init(tcp_vegas_register); +module_exit(tcp_vegas_unregister); + +MODULE_AUTHOR("Stephen Hemminger"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Vegas"); diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c new file mode 100644 index 000000000000..ef827242c940 --- /dev/null +++ b/net/ipv4/tcp_westwood.c @@ -0,0 +1,259 @@ +/* + * TCP Westwood+ + * + * Angelo Dell'Aera: TCP Westwood+ support + */ + +#include +#include +#include +#include +#include +#include + +/* TCP Westwood structure */ +struct westwood { + u32 bw_ns_est; /* first bandwidth estimation..not too smoothed 8) */ + u32 bw_est; /* bandwidth estimate */ + u32 rtt_win_sx; /* here starts a new evaluation... */ + u32 bk; + u32 snd_una; /* used for evaluating the number of acked bytes */ + u32 cumul_ack; + u32 accounted; + u32 rtt; + u32 rtt_min; /* minimum observed RTT */ +}; + + +/* TCP Westwood functions and constants */ +#define TCP_WESTWOOD_RTT_MIN (HZ/20) /* 50ms */ +#define TCP_WESTWOOD_INIT_RTT (20*HZ) /* maybe too conservative?! */ + +/* + * @tcp_westwood_create + * This function initializes fields used in TCP Westwood+, + * it is called after the initial SYN, so the sequence numbers + * are correct but new passive connections we have no + * information about RTTmin at this time so we simply set it to + * TCP_WESTWOOD_INIT_RTT. This value was chosen to be too conservative + * since in this way we're sure it will be updated in a consistent + * way as soon as possible. It will reasonably happen within the first + * RTT period of the connection lifetime. + */ +static void tcp_westwood_init(struct tcp_sock *tp) +{ + struct westwood *w = tcp_ca(tp); + + w->bk = 0; + w->bw_ns_est = 0; + w->bw_est = 0; + w->accounted = 0; + w->cumul_ack = 0; + w->rtt_min = w->rtt = TCP_WESTWOOD_INIT_RTT; + w->rtt_win_sx = tcp_time_stamp; + w->snd_una = tp->snd_una; +} + +/* + * @westwood_do_filter + * Low-pass filter. Implemented using constant coefficients. + */ +static inline u32 westwood_do_filter(u32 a, u32 b) +{ + return (((7 * a) + b) >> 3); +} + +static inline void westwood_filter(struct westwood *w, u32 delta) +{ + w->bw_ns_est = westwood_do_filter(w->bw_ns_est, w->bk / delta); + w->bw_est = westwood_do_filter(w->bw_est, w->bw_ns_est); +} + +/* + * @westwood_pkts_acked + * Called after processing group of packets. + * but all westwood needs is the last sample of srtt. + */ +static void tcp_westwood_pkts_acked(struct tcp_sock *tp, u32 cnt) +{ + struct westwood *w = tcp_ca(tp); + if (cnt > 0) + w->rtt = tp->srtt >> 3; +} + +/* + * @westwood_update_window + * It updates RTT evaluation window if it is the right moment to do + * it. If so it calls filter for evaluating bandwidth. + */ +static void westwood_update_window(struct tcp_sock *tp) +{ + struct westwood *w = tcp_ca(tp); + s32 delta = tcp_time_stamp - w->rtt_win_sx; + + /* + * See if a RTT-window has passed. + * Be careful since if RTT is less than + * 50ms we don't filter but we continue 'building the sample'. + * This minimum limit was chosen since an estimation on small + * time intervals is better to avoid... + * Obviously on a LAN we reasonably will always have + * right_bound = left_bound + WESTWOOD_RTT_MIN + */ + if (w->rtt && delta > max_t(u32, w->rtt, TCP_WESTWOOD_RTT_MIN)) { + westwood_filter(w, delta); + + w->bk = 0; + w->rtt_win_sx = tcp_time_stamp; + } +} + +/* + * @westwood_fast_bw + * It is called when we are in fast path. In particular it is called when + * header prediction is successful. In such case in fact update is + * straight forward and doesn't need any particular care. + */ +static inline void westwood_fast_bw(struct tcp_sock *tp) +{ + struct westwood *w = tcp_ca(tp); + + westwood_update_window(tp); + + w->bk += tp->snd_una - w->snd_una; + w->snd_una = tp->snd_una; + w->rtt_min = min(w->rtt, w->rtt_min); +} + +/* + * @westwood_acked_count + * This function evaluates cumul_ack for evaluating bk in case of + * delayed or partial acks. + */ +static inline u32 westwood_acked_count(struct tcp_sock *tp) +{ + struct westwood *w = tcp_ca(tp); + + w->cumul_ack = tp->snd_una - w->snd_una; + + /* If cumul_ack is 0 this is a dupack since it's not moving + * tp->snd_una. + */ + if (!w->cumul_ack) { + w->accounted += tp->mss_cache; + w->cumul_ack = tp->mss_cache; + } + + if (w->cumul_ack > tp->mss_cache) { + /* Partial or delayed ack */ + if (w->accounted >= w->cumul_ack) { + w->accounted -= w->cumul_ack; + w->cumul_ack = tp->mss_cache; + } else { + w->cumul_ack -= w->accounted; + w->accounted = 0; + } + } + + w->snd_una = tp->snd_una; + + return w->cumul_ack; +} + +static inline u32 westwood_bw_rttmin(const struct tcp_sock *tp) +{ + struct westwood *w = tcp_ca(tp); + return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2); +} + +/* + * TCP Westwood + * Here limit is evaluated as Bw estimation*RTTmin (for obtaining it + * in packets we use mss_cache). Rttmin is guaranteed to be >= 2 + * so avoids ever returning 0. + */ +static u32 tcp_westwood_cwnd_min(struct tcp_sock *tp) +{ + return westwood_bw_rttmin(tp); +} + +static void tcp_westwood_event(struct tcp_sock *tp, enum tcp_ca_event event) +{ + struct westwood *w = tcp_ca(tp); + + switch(event) { + case CA_EVENT_FAST_ACK: + westwood_fast_bw(tp); + break; + + case CA_EVENT_COMPLETE_CWR: + tp->snd_cwnd = tp->snd_ssthresh = westwood_bw_rttmin(tp); + break; + + case CA_EVENT_FRTO: + tp->snd_ssthresh = westwood_bw_rttmin(tp); + break; + + case CA_EVENT_SLOW_ACK: + westwood_update_window(tp); + w->bk += westwood_acked_count(tp); + w->rtt_min = min(w->rtt, w->rtt_min); + break; + + default: + /* don't care */ + break; + } +} + + +/* Extract info for Tcp socket info provided via netlink. */ +static void tcp_westwood_info(struct tcp_sock *tp, u32 ext, + struct sk_buff *skb) +{ + const struct westwood *ca = tcp_ca(tp); + if (ext & (1<<(TCPDIAG_VEGASINFO-1))) { + struct rtattr *rta; + struct tcpvegas_info *info; + + rta = __RTA_PUT(skb, TCPDIAG_VEGASINFO, sizeof(*info)); + info = RTA_DATA(rta); + info->tcpv_enabled = 1; + info->tcpv_rttcnt = 0; + info->tcpv_rtt = jiffies_to_usecs(ca->rtt); + info->tcpv_minrtt = jiffies_to_usecs(ca->rtt_min); + rtattr_failure: ; + } +} + + +static struct tcp_congestion_ops tcp_westwood = { + .init = tcp_westwood_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = tcp_reno_cong_avoid, + .min_cwnd = tcp_westwood_cwnd_min, + .cwnd_event = tcp_westwood_event, + .get_info = tcp_westwood_info, + .pkts_acked = tcp_westwood_pkts_acked, + + .owner = THIS_MODULE, + .name = "westwood" +}; + +static int __init tcp_westwood_register(void) +{ + BUG_ON(sizeof(struct westwood) > TCP_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_westwood); +} + +static void __exit tcp_westwood_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_westwood); +} + +module_init(tcp_westwood_register); +module_exit(tcp_westwood_unregister); + +MODULE_AUTHOR("Stephen Hemminger, Angelo Dell'Aera"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Westwood+"); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 14f5c53235fe..a54d4ef3fd35 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -57,6 +57,7 @@ #endif #include #include +#include #include #include @@ -3437,7 +3438,7 @@ static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf * by sysctl and we wouldn't want anyone to change it under our feet * (see SIOCSIFNAME). */ - dev_name = net_sysctl_strdup(dev_name); + dev_name = kstrdup(dev_name, GFP_KERNEL); if (!dev_name) goto free; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2414937f2a83..9dac7fdf4726 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -2025,7 +2025,7 @@ static int tcp_v6_init_sock(struct sock *sk) sk->sk_state = TCP_CLOSE; tp->af_specific = &ipv6_specific; - + tp->ca_ops = &tcp_init_congestion_ops; sk->sk_write_space = sk_stream_write_space; sock_set_flag(sk, SOCK_USE_WRITE_QUEUE); diff --git a/net/sched/Kconfig b/net/sched/Kconfig index b22c9beb604d..447b89e556b1 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -449,6 +449,18 @@ config NET_EMATCH_META To compile this code as a module, choose M here: the module will be called em_meta. +config NET_EMATCH_TEXT + tristate "Textsearch" + depends on NET_EMATCH + select TEXTSEARCH + ---help--- + Say Y here if you want to be ablt to classify packets based on + textsearch comparisons. Please select the appropriate textsearch + algorithms in the Library section. + + To compile this code as a module, choose M here: the + module will be called em_text. + config NET_CLS_ACT bool "Packet ACTION" depends on EXPERIMENTAL && NET_CLS && NET_QOS diff --git a/net/sched/Makefile b/net/sched/Makefile index eb3fe583eba8..8f58cecd6266 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_NET_EMATCH_CMP) += em_cmp.o obj-$(CONFIG_NET_EMATCH_NBYTE) += em_nbyte.o obj-$(CONFIG_NET_EMATCH_U32) += em_u32.o obj-$(CONFIG_NET_EMATCH_META) += em_meta.o +obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o diff --git a/net/sched/em_text.c b/net/sched/em_text.c new file mode 100644 index 000000000000..873840d8d072 --- /dev/null +++ b/net/sched/em_text.c @@ -0,0 +1,157 @@ +/* + * net/sched/em_text.c Textsearch ematch + * + * 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: Thomas Graf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct text_match +{ + u16 from_offset; + u16 to_offset; + u8 from_layer; + u8 to_layer; + struct ts_config *config; +}; + +#define EM_TEXT_PRIV(m) ((struct text_match *) (m)->data) + +static int em_text_match(struct sk_buff *skb, struct tcf_ematch *m, + struct tcf_pkt_info *info) +{ + struct text_match *tm = EM_TEXT_PRIV(m); + int from, to; + struct ts_state state; + + from = tcf_get_base_ptr(skb, tm->from_layer) - skb->data; + from += tm->from_offset; + + to = tcf_get_base_ptr(skb, tm->to_layer) - skb->data; + to += tm->to_offset; + + return skb_find_text(skb, from, to, tm->config, &state) != UINT_MAX; +} + +static int em_text_change(struct tcf_proto *tp, void *data, int len, + struct tcf_ematch *m) +{ + struct text_match *tm; + struct tcf_em_text *conf = data; + struct ts_config *ts_conf; + int flags = 0; + + printk("Configuring text: %s from %d:%d to %d:%d len %d\n", conf->algo, conf->from_offset, + conf->from_layer, conf->to_offset, conf->to_layer, conf->pattern_len); + + if (len < sizeof(*conf) || len < (sizeof(*conf) + conf->pattern_len)) + return -EINVAL; + + if (conf->from_layer > conf->to_layer) + return -EINVAL; + + if (conf->from_layer == conf->to_layer && + conf->from_offset > conf->to_offset) + return -EINVAL; + +retry: + ts_conf = textsearch_prepare(conf->algo, (u8 *) conf + sizeof(*conf), + conf->pattern_len, GFP_KERNEL, flags); + + if (flags & TS_AUTOLOAD) + rtnl_lock(); + + if (IS_ERR(ts_conf)) { + if (PTR_ERR(ts_conf) == -ENOENT && !(flags & TS_AUTOLOAD)) { + rtnl_unlock(); + flags |= TS_AUTOLOAD; + goto retry; + } else + return PTR_ERR(ts_conf); + } else if (flags & TS_AUTOLOAD) { + textsearch_destroy(ts_conf); + return -EAGAIN; + } + + tm = kmalloc(sizeof(*tm), GFP_KERNEL); + if (tm == NULL) { + textsearch_destroy(ts_conf); + return -ENOBUFS; + } + + tm->from_offset = conf->from_offset; + tm->to_offset = conf->to_offset; + tm->from_layer = conf->from_layer; + tm->to_layer = conf->to_layer; + tm->config = ts_conf; + + m->datalen = sizeof(*tm); + m->data = (unsigned long) tm; + + return 0; +} + +static void em_text_destroy(struct tcf_proto *tp, struct tcf_ematch *m) +{ + textsearch_destroy(EM_TEXT_PRIV(m)->config); +} + +static int em_text_dump(struct sk_buff *skb, struct tcf_ematch *m) +{ + struct text_match *tm = EM_TEXT_PRIV(m); + struct tcf_em_text conf; + + strncpy(conf.algo, tm->config->ops->name, sizeof(conf.algo) - 1); + conf.from_offset = tm->from_offset; + conf.to_offset = tm->to_offset; + conf.from_layer = tm->from_layer; + conf.to_layer = tm->to_layer; + conf.pattern_len = textsearch_get_pattern_len(tm->config); + conf.pad = 0; + + RTA_PUT_NOHDR(skb, sizeof(conf), &conf); + RTA_APPEND(skb, conf.pattern_len, textsearch_get_pattern(tm->config)); + return 0; + +rtattr_failure: + return -1; +} + +static struct tcf_ematch_ops em_text_ops = { + .kind = TCF_EM_TEXT, + .change = em_text_change, + .match = em_text_match, + .destroy = em_text_destroy, + .dump = em_text_dump, + .owner = THIS_MODULE, + .link = LIST_HEAD_INIT(em_text_ops.link) +}; + +static int __init init_em_text(void) +{ + return tcf_em_register(&em_text_ops); +} + +static void __exit exit_em_text(void) +{ + tcf_em_unregister(&em_text_ops); +} + +MODULE_LICENSE("GPL"); + +module_init(init_em_text); +module_exit(exit_em_text); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 058189684c7c..86073df418f5 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -92,6 +92,17 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); +static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, + __u16 error, + const struct sctp_association *asoc, + struct sctp_transport *transport); + +static sctp_disposition_t sctp_sf_violation_chunklen( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands); /* Small helper function that checks if the chunk length * is of the appropriate length. The 'required_length' argument @@ -2328,7 +2339,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, * * This is common code called by several sctp_sf_*_abort() functions above. */ -sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, +static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, __u16 error, const struct sctp_association *asoc, struct sctp_transport *transport) @@ -3687,7 +3698,8 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, * * Generate an ABORT chunk and terminate the association. */ -sctp_disposition_t sctp_sf_violation_chunklen(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_violation_chunklen( + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 32e8acbc60fe..62a073495276 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -41,6 +41,7 @@ EXPORT_SYMBOL(rpc_release_task); /* RPC client functions */ EXPORT_SYMBOL(rpc_create_client); +EXPORT_SYMBOL(rpc_new_client); EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_bind_new_program); EXPORT_SYMBOL(rpc_destroy_client); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 2b99b4028d31..d6baf6fdf8a9 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -8,6 +8,7 @@ #include #include #include +#include #define RPCDBG_FACILITY RPCDBG_AUTH @@ -20,14 +21,6 @@ */ -static char *strdup(char *s) -{ - char *rv = kmalloc(strlen(s)+1, GFP_KERNEL); - if (rv) - strcpy(rv, s); - return rv; -} - struct unix_domain { struct auth_domain h; int addr_changes; @@ -55,7 +48,7 @@ struct auth_domain *unix_domain_find(char *name) if (new == NULL) return NULL; cache_init(&new->h.h); - new->h.name = strdup(name); + new->h.name = kstrdup(name, GFP_KERNEL); new->h.flavour = RPC_AUTH_UNIX; new->addr_changes = 0; new->h.h.expiry_time = NEVER; diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 352d531ee3c1..76ba6be3dfc9 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -176,10 +176,10 @@ endif define rule_cc_o_c $(if $($(quiet)cmd_checksrc),echo ' $($(quiet)cmd_checksrc)';) \ $(cmd_checksrc) \ - $(if $($(quiet)cmd_cc_o_c),echo ' $($(quiet)cmd_cc_o_c)';) \ + $(if $($(quiet)cmd_cc_o_c),echo ' $(subst ','\'',$($(quiet)cmd_cc_o_c))';) \ $(cmd_cc_o_c); \ $(cmd_modversions) \ - scripts/basic/fixdep $(depfile) $@ '$(cmd_cc_o_c)' > $(@D)/.$(@F).tmp; \ + scripts/basic/fixdep $(depfile) $@ '$(subst ','\'',$(cmd_cc_o_c))' > $(@D)/.$(@F).tmp; \ rm -f $(depfile); \ mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd endef diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 1e1736d81ee9..ace4706ab251 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -432,8 +432,8 @@ True - GTK_POLICY_ALWAYS - GTK_POLICY_ALWAYS + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT @@ -466,8 +466,8 @@ True - GTK_POLICY_ALWAYS - GTK_POLICY_ALWAYS + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT @@ -496,7 +496,7 @@ True GTK_POLICY_NEVER - GTK_POLICY_ALWAYS + GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT diff --git a/security/commoncap.c b/security/commoncap.c index 849b8c338ee8..04c12f58d656 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -149,7 +149,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || !cap_issubset (new_permitted, current->cap_permitted)) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { if (!capable(CAP_SETUID)) { diff --git a/security/dummy.c b/security/dummy.c index b32eff146547..6ff887586479 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -130,7 +130,7 @@ static void dummy_bprm_free_security (struct linux_binprm *bprm) static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { - current->mm->dumpable = 0; + current->mm->dumpable = suid_dumpable; if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) { bprm->e_uid = current->uid; diff --git a/security/keys/Makefile b/security/keys/Makefile index ddb495d65062..c392d750b208 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -7,8 +7,9 @@ obj-y := \ keyring.o \ keyctl.o \ process_keys.o \ - user_defined.o \ - request_key.o + request_key.o \ + request_key_auth.o \ + user_defined.o obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/security/keys/compat.c b/security/keys/compat.c index aff8b22dcb5c..3303673c636e 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -1,6 +1,6 @@ /* compat.c: 32-bit compatibility syscall for 64-bit systems * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -24,7 +24,7 @@ * - if you can, you should call sys_keyctl directly */ asmlinkage long compat_sys_keyctl(u32 option, - u32 arg2, u32 arg3, u32 arg4, u32 arg5) + u32 arg2, u32 arg3, u32 arg4, u32 arg5) { switch (option) { case KEYCTL_GET_KEYRING_ID: @@ -71,6 +71,9 @@ asmlinkage long compat_sys_keyctl(u32 option, case KEYCTL_NEGATE: return keyctl_negate_key(arg2, arg3, arg4); + case KEYCTL_SET_REQKEY_KEYRING: + return keyctl_set_reqkey_keyring(arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 67b2b93a7489..46c8602661c9 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -1,6 +1,6 @@ /* internal.h: authentication token and access key management internal defs * - * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -15,6 +15,16 @@ #include #include +#if 0 +#define kenter(FMT, a...) printk("==> %s("FMT")\n",__FUNCTION__ , ## a) +#define kleave(FMT, a...) printk("<== %s()"FMT"\n",__FUNCTION__ , ## a) +#define kdebug(FMT, a...) printk(FMT"\n" , ## a) +#else +#define kenter(FMT, a...) do {} while(0) +#define kleave(FMT, a...) do {} while(0) +#define kdebug(FMT, a...) do {} while(0) +#endif + extern struct key_type key_type_dead; extern struct key_type key_type_user; @@ -66,20 +76,46 @@ extern struct key *__keyring_search_one(struct key *keyring, const char *description, key_perm_t perm); +extern struct key *keyring_search_instkey(struct key *keyring, + key_serial_t target_id); + typedef int (*key_match_func_t)(const struct key *, const void *); extern struct key *keyring_search_aux(struct key *keyring, + struct task_struct *tsk, struct key_type *type, const void *description, key_match_func_t match); -extern struct key *search_process_keyrings_aux(struct key_type *type, - const void *description, - key_match_func_t match); +extern struct key *search_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + struct task_struct *tsk); extern struct key *find_keyring_by_name(const char *name, key_serial_t bound); extern int install_thread_keyring(struct task_struct *tsk); +extern int install_process_keyring(struct task_struct *tsk); + +extern struct key *request_key_and_link(struct key_type *type, + const char *description, + const char *callout_info, + struct key *dest_keyring); + +/* + * request_key authorisation + */ +struct request_key_auth { + struct key *target_key; + struct task_struct *context; + pid_t pid; +}; + +extern struct key_type key_type_request_key_auth; +extern struct key *request_key_auth_new(struct key *target, + struct key **_rkakey); + +extern struct key *key_get_instantiation_authkey(key_serial_t target_id); /* * keyctl functions @@ -100,6 +136,7 @@ extern long keyctl_setperm_key(key_serial_t, key_perm_t); extern long keyctl_instantiate_key(key_serial_t, const void __user *, size_t, key_serial_t); extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t); +extern long keyctl_set_reqkey_keyring(int); /* diff --git a/security/keys/key.c b/security/keys/key.c index 59402c843203..3304d37bb379 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1,6 +1,6 @@ /* key.c: basic authentication token and access key management * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -294,7 +294,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, } atomic_set(&key->usage, 1); - rwlock_init(&key->lock); init_rwsem(&key->sem); key->type = type; key->user = user; @@ -308,7 +307,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->payload.data = NULL; if (!not_in_quota) - key->flags |= KEY_FLAG_IN_QUOTA; + key->flags |= 1 << KEY_FLAG_IN_QUOTA; memset(&key->type_data, 0, sizeof(key->type_data)); @@ -359,7 +358,7 @@ int key_payload_reserve(struct key *key, size_t datalen) key_check(key); /* contemplate the quota adjustment */ - if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) { + if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { spin_lock(&key->user->lock); if (delta > 0 && @@ -392,7 +391,8 @@ EXPORT_SYMBOL(key_payload_reserve); static int __key_instantiate_and_link(struct key *key, const void *data, size_t datalen, - struct key *keyring) + struct key *keyring, + struct key *instkey) { int ret, awaken; @@ -405,27 +405,25 @@ static int __key_instantiate_and_link(struct key *key, down_write(&key_construction_sem); /* can't instantiate twice */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* instantiate the key */ ret = key->type->instantiate(key, data, datalen); if (ret == 0) { /* mark the key as being instantiated */ - write_lock(&key->lock); - atomic_inc(&key->user->nikeys); - key->flags |= KEY_FLAG_INSTANTIATED; + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); - if (key->flags & KEY_FLAG_USER_CONSTRUCT) { - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) awaken = 1; - } - - write_unlock(&key->lock); /* and link it into the destination keyring */ if (keyring) ret = __key_link(keyring, key); + + /* disable the authorisation key */ + if (instkey) + key_revoke(instkey); } } @@ -446,19 +444,21 @@ static int __key_instantiate_and_link(struct key *key, int key_instantiate_and_link(struct key *key, const void *data, size_t datalen, - struct key *keyring) + struct key *keyring, + struct key *instkey) { int ret; if (keyring) down_write(&keyring->sem); - ret = __key_instantiate_and_link(key, data, datalen, keyring); + ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey); if (keyring) up_write(&keyring->sem); return ret; + } /* end key_instantiate_and_link() */ EXPORT_SYMBOL(key_instantiate_and_link); @@ -469,7 +469,8 @@ EXPORT_SYMBOL(key_instantiate_and_link); */ int key_negate_and_link(struct key *key, unsigned timeout, - struct key *keyring) + struct key *keyring, + struct key *instkey) { struct timespec now; int ret, awaken; @@ -486,26 +487,26 @@ int key_negate_and_link(struct key *key, down_write(&key_construction_sem); /* can't instantiate twice */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* mark the key as being negatively instantiated */ - write_lock(&key->lock); - atomic_inc(&key->user->nikeys); - key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; + set_bit(KEY_FLAG_NEGATIVE, &key->flags); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); now = current_kernel_time(); key->expiry = now.tv_sec + timeout; - if (key->flags & KEY_FLAG_USER_CONSTRUCT) { - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) awaken = 1; - } - write_unlock(&key->lock); ret = 0; /* and link it into the destination keyring */ if (keyring) ret = __key_link(keyring, key); + + /* disable the authorisation key */ + if (instkey) + key_revoke(instkey); } up_write(&key_construction_sem); @@ -553,8 +554,10 @@ static void key_cleanup(void *data) rb_erase(&key->serial_node, &key_serial_tree); spin_unlock(&key_serial_lock); + key_check(key); + /* deal with the user's key tracking and quota */ - if (key->flags & KEY_FLAG_IN_QUOTA) { + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { spin_lock(&key->user->lock); key->user->qnkeys--; key->user->qnbytes -= key->quotalen; @@ -562,7 +565,7 @@ static void key_cleanup(void *data) } atomic_dec(&key->user->nkeys); - if (key->flags & KEY_FLAG_INSTANTIATED) + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) atomic_dec(&key->user->nikeys); key_user_put(key->user); @@ -631,9 +634,9 @@ struct key *key_lookup(key_serial_t id) goto error; found: - /* pretent doesn't exist if it's dead */ + /* pretend it doesn't exist if it's dead */ if (atomic_read(&key->usage) == 0 || - (key->flags & KEY_FLAG_DEAD) || + test_bit(KEY_FLAG_DEAD, &key->flags) || key->type == &key_type_dead) goto not_found; @@ -708,12 +711,9 @@ static inline struct key *__key_update(struct key *key, const void *payload, ret = key->type->update(key, payload, plen); - if (ret == 0) { + if (ret == 0) /* updating a negative key instantiates it */ - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_NEGATIVE; - write_unlock(&key->lock); - } + clear_bit(KEY_FLAG_NEGATIVE, &key->flags); up_write(&key->sem); @@ -793,7 +793,7 @@ struct key *key_create_or_update(struct key *keyring, } /* instantiate it and link it into the target keyring */ - ret = __key_instantiate_and_link(key, payload, plen, keyring); + ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL); if (ret < 0) { key_put(key); key = ERR_PTR(ret); @@ -841,12 +841,9 @@ int key_update(struct key *key, const void *payload, size_t plen) down_write(&key->sem); ret = key->type->update(key, payload, plen); - if (ret == 0) { + if (ret == 0) /* updating a negative key instantiates it */ - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_NEGATIVE; - write_unlock(&key->lock); - } + clear_bit(KEY_FLAG_NEGATIVE, &key->flags); up_write(&key->sem); } @@ -892,10 +889,7 @@ struct key *key_duplicate(struct key *source, const char *desc) goto error2; atomic_inc(&key->user->nikeys); - - write_lock(&key->lock); - key->flags |= KEY_FLAG_INSTANTIATED; - write_unlock(&key->lock); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); error_k: up_read(&key_types_sem); @@ -922,9 +916,7 @@ void key_revoke(struct key *key) /* make sure no one's trying to change or use the key when we mark * it */ down_write(&key->sem); - write_lock(&key->lock); - key->flags |= KEY_FLAG_REVOKED; - write_unlock(&key->lock); + set_bit(KEY_FLAG_REVOKED, &key->flags); up_write(&key->sem); } /* end key_revoke() */ @@ -975,24 +967,33 @@ void unregister_key_type(struct key_type *ktype) /* withdraw the key type */ list_del_init(&ktype->link); - /* need to withdraw all keys of this type */ + /* mark all the keys of this type dead */ spin_lock(&key_serial_lock); for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { key = rb_entry(_n, struct key, serial_node); - if (key->type != ktype) - continue; + if (key->type == ktype) + key->type = &key_type_dead; + } - write_lock(&key->lock); - key->type = &key_type_dead; - write_unlock(&key->lock); + spin_unlock(&key_serial_lock); - /* there shouldn't be anyone looking at the description or - * payload now */ - if (ktype->destroy) - ktype->destroy(key); - memset(&key->payload, 0xbd, sizeof(key->payload)); + /* make sure everyone revalidates their keys */ + synchronize_kernel(); + + /* we should now be able to destroy the payloads of all the keys of + * this type with impunity */ + spin_lock(&key_serial_lock); + + for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { + key = rb_entry(_n, struct key, serial_node); + + if (key->type == ktype) { + if (ktype->destroy) + ktype->destroy(key); + memset(&key->payload, 0xbd, sizeof(key->payload)); + } } spin_unlock(&key_serial_lock); @@ -1037,4 +1038,5 @@ void __init key_init(void) /* link the two root keyrings together */ key_link(&root_session_keyring, &root_user_keyring); + } /* end key_init() */ diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index dc0011b3fac9..fea262860ea0 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1,6 +1,6 @@ /* keyctl.c: userspace keyctl operations * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -49,6 +49,13 @@ asmlinkage long sys_add_key(const char __user *_type, goto error; type[31] = '\0'; + if (!type[0]) + goto error; + + ret = -EPERM; + if (type[0] == '.') + goto error; + ret = -EFAULT; dlen = strnlen_user(_description, PAGE_SIZE - 1); if (dlen <= 0) @@ -82,7 +89,7 @@ asmlinkage long sys_add_key(const char __user *_type, } /* find the target keyring (which must be writable) */ - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error3; @@ -181,7 +188,7 @@ asmlinkage long sys_request_key(const char __user *_type, /* get the destination keyring if specified */ dest = NULL; if (destringid) { - dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); + dest = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); if (IS_ERR(dest)) { ret = PTR_ERR(dest); goto error3; @@ -196,23 +203,15 @@ asmlinkage long sys_request_key(const char __user *_type, } /* do the search */ - key = request_key(ktype, description, callout_info); + key = request_key_and_link(ktype, description, callout_info, dest); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error5; } - /* link the resulting key to the destination keyring */ - if (dest) { - ret = key_link(dest, key); - if (ret < 0) - goto error6; - } - ret = key->serial; - error6: - key_put(key); + key_put(key); error5: key_type_put(ktype); error4: @@ -237,7 +236,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create) struct key *key; long ret; - key = lookup_user_key(id, create, 0, KEY_SEARCH); + key = lookup_user_key(NULL, id, create, 0, KEY_SEARCH); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -324,7 +323,7 @@ long keyctl_update_key(key_serial_t id, } /* find the target key (which must be writable) */ - key = lookup_user_key(id, 0, 0, KEY_WRITE); + key = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error2; @@ -352,7 +351,7 @@ long keyctl_revoke_key(key_serial_t id) struct key *key; long ret; - key = lookup_user_key(id, 0, 0, KEY_WRITE); + key = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -378,7 +377,7 @@ long keyctl_keyring_clear(key_serial_t ringid) struct key *keyring; long ret; - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; @@ -404,13 +403,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) struct key *keyring, *key; long ret; - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; } - key = lookup_user_key(id, 1, 0, KEY_LINK); + key = lookup_user_key(NULL, id, 1, 0, KEY_LINK); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error2; @@ -438,13 +437,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) struct key *keyring, *key; long ret; - keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; } - key = lookup_user_key(id, 0, 0, 0); + key = lookup_user_key(NULL, id, 0, 0, 0); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error2; @@ -475,16 +474,29 @@ long keyctl_describe_key(key_serial_t keyid, char __user *buffer, size_t buflen) { - struct key *key; + struct key *key, *instkey; char *tmpbuf; long ret; - key = lookup_user_key(keyid, 0, 1, KEY_VIEW); + key = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW); if (IS_ERR(key)) { + /* viewing a key under construction is permitted if we have the + * authorisation token handy */ + if (PTR_ERR(key) == -EACCES) { + instkey = key_get_instantiation_authkey(keyid); + if (!IS_ERR(instkey)) { + key_put(instkey); + key = lookup_user_key(NULL, keyid, 0, 1, 0); + if (!IS_ERR(key)) + goto okay; + } + } + ret = PTR_ERR(key); goto error; } +okay: /* calculate how much description we're going to return */ ret = -ENOMEM; tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); @@ -568,7 +580,7 @@ long keyctl_keyring_search(key_serial_t ringid, goto error2; /* get the keyring at which to begin the search */ - keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH); + keyring = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; @@ -577,7 +589,7 @@ long keyctl_keyring_search(key_serial_t ringid, /* get the destination keyring if specified */ dest = NULL; if (destringid) { - dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); + dest = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); if (IS_ERR(dest)) { ret = PTR_ERR(dest); goto error3; @@ -656,24 +668,23 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) long ret; /* find the key first */ - key = lookup_user_key(keyid, 0, 0, 0); + key = lookup_user_key(NULL, keyid, 0, 0, 0); if (!IS_ERR(key)) { /* see if we can read it directly */ if (key_permission(key, KEY_READ)) goto can_read_key; - /* can't; see if it's searchable from this process's - * keyrings */ - ret = -ENOKEY; - if (key_permission(key, KEY_SEARCH)) { - /* okay - we do have search permission on the key - * itself, but do we have the key? */ - skey = search_process_keyrings_aux(key->type, key, - keyctl_read_key_same); - if (!IS_ERR(skey)) - goto can_read_key2; - } + /* we can't; see if it's searchable from this process's + * keyrings + * - we automatically take account of the fact that it may be + * dangling off an instantiation key + */ + skey = search_process_keyrings(key->type, key, + keyctl_read_key_same, current); + if (!IS_ERR(skey)) + goto can_read_key2; + ret = PTR_ERR(skey); goto error2; } @@ -719,7 +730,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) if (uid == (uid_t) -1 && gid == (gid_t) -1) goto error; - key = lookup_user_key(id, 1, 1, 0); + key = lookup_user_key(NULL, id, 1, 1, 0); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; @@ -728,7 +739,6 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) /* make the changes with the locks held to prevent chown/chown races */ ret = -EACCES; down_write(&key->sem); - write_lock(&key->lock); if (!capable(CAP_SYS_ADMIN)) { /* only the sysadmin can chown a key to some other UID */ @@ -755,7 +765,6 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) ret = 0; no_access: - write_unlock(&key->lock); up_write(&key->sem); key_put(key); error: @@ -778,32 +787,25 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) goto error; - key = lookup_user_key(id, 1, 1, 0); + key = lookup_user_key(NULL, id, 1, 1, 0); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } - /* make the changes with the locks held to prevent chown/chmod - * races */ + /* make the changes with the locks held to prevent chown/chmod races */ ret = -EACCES; down_write(&key->sem); - write_lock(&key->lock); - /* if we're not the sysadmin, we can only chmod a key that we - * own */ - if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid) - goto no_access; + /* if we're not the sysadmin, we can only change a key that we own */ + if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) { + key->perm = perm; + ret = 0; + } - /* changing the permissions mask */ - key->perm = perm; - ret = 0; - - no_access: - write_unlock(&key->lock); up_write(&key->sem); key_put(key); - error: +error: return ret; } /* end keyctl_setperm_key() */ @@ -818,7 +820,8 @@ long keyctl_instantiate_key(key_serial_t id, size_t plen, key_serial_t ringid) { - struct key *key, *keyring; + struct request_key_auth *rka; + struct key *instkey, *keyring; void *payload; long ret; @@ -840,18 +843,21 @@ long keyctl_instantiate_key(key_serial_t id, goto error2; } - /* find the target key (which must be writable) */ - key = lookup_user_key(id, 0, 1, KEY_WRITE); - if (IS_ERR(key)) { - ret = PTR_ERR(key); + /* find the instantiation authorisation key */ + instkey = key_get_instantiation_authkey(id); + if (IS_ERR(instkey)) { + ret = PTR_ERR(instkey); goto error2; } - /* find the destination keyring if present (which must also be - * writable) */ + rka = instkey->payload.data; + + /* find the destination keyring amongst those belonging to the + * requesting task */ keyring = NULL; if (ringid) { - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(rka->context, ringid, 1, 0, + KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error3; @@ -859,11 +865,12 @@ long keyctl_instantiate_key(key_serial_t id, } /* instantiate the key and link it into a keyring */ - ret = key_instantiate_and_link(key, payload, plen, keyring); + ret = key_instantiate_and_link(rka->target_key, payload, plen, + keyring, instkey); key_put(keyring); error3: - key_put(key); + key_put(instkey); error2: kfree(payload); error: @@ -878,21 +885,24 @@ long keyctl_instantiate_key(key_serial_t id, */ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) { - struct key *key, *keyring; + struct request_key_auth *rka; + struct key *instkey, *keyring; long ret; - /* find the target key (which must be writable) */ - key = lookup_user_key(id, 0, 1, KEY_WRITE); - if (IS_ERR(key)) { - ret = PTR_ERR(key); + /* find the instantiation authorisation key */ + instkey = key_get_instantiation_authkey(id); + if (IS_ERR(instkey)) { + ret = PTR_ERR(instkey); goto error; } + rka = instkey->payload.data; + /* find the destination keyring if present (which must also be * writable) */ keyring = NULL; if (ringid) { - keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + keyring = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; @@ -900,16 +910,54 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) } /* instantiate the key and link it into a keyring */ - ret = key_negate_and_link(key, timeout, keyring); + ret = key_negate_and_link(rka->target_key, timeout, keyring, instkey); key_put(keyring); error2: - key_put(key); + key_put(instkey); error: return ret; } /* end keyctl_negate_key() */ +/*****************************************************************************/ +/* + * set the default keyring in which request_key() will cache keys + * - return the old setting + */ +long keyctl_set_reqkey_keyring(int reqkey_defl) +{ + int ret; + + switch (reqkey_defl) { + case KEY_REQKEY_DEFL_THREAD_KEYRING: + ret = install_thread_keyring(current); + if (ret < 0) + return ret; + goto set; + + case KEY_REQKEY_DEFL_PROCESS_KEYRING: + ret = install_process_keyring(current); + if (ret < 0) + return ret; + + case KEY_REQKEY_DEFL_DEFAULT: + case KEY_REQKEY_DEFL_SESSION_KEYRING: + case KEY_REQKEY_DEFL_USER_KEYRING: + case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: + set: + current->jit_keyring = reqkey_defl; + + case KEY_REQKEY_DEFL_NO_CHANGE: + return current->jit_keyring; + + case KEY_REQKEY_DEFL_GROUP_KEYRING: + default: + return -EINVAL; + } + +} /* end keyctl_set_reqkey_keyring() */ + /*****************************************************************************/ /* * the key control system call @@ -980,6 +1028,9 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, (unsigned) arg3, (key_serial_t) arg4); + case KEYCTL_SET_REQKEY_KEYRING: + return keyctl_set_reqkey_keyring(arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index e2ab4f8e7481..90a551e4da66 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1,6 +1,6 @@ /* keyring.c: keyring handling * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -132,10 +132,17 @@ static int keyring_duplicate(struct key *keyring, const struct key *source) (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key); ret = 0; - sklist = source->payload.subscriptions; - if (sklist && sklist->nkeys > 0) { + /* find out how many keys are currently linked */ + rcu_read_lock(); + sklist = rcu_dereference(source->payload.subscriptions); + max = 0; + if (sklist) max = sklist->nkeys; + rcu_read_unlock(); + + /* allocate a new payload and stuff load with key links */ + if (max > 0) { BUG_ON(max > limit); max = (max + 3) & ~3; @@ -148,6 +155,10 @@ static int keyring_duplicate(struct key *keyring, const struct key *source) if (!klist) goto error; + /* set links */ + rcu_read_lock(); + sklist = rcu_dereference(source->payload.subscriptions); + klist->maxkeys = max; klist->nkeys = sklist->nkeys; memcpy(klist->keys, @@ -157,7 +168,9 @@ static int keyring_duplicate(struct key *keyring, const struct key *source) for (loop = klist->nkeys - 1; loop >= 0; loop--) atomic_inc(&klist->keys[loop]->usage); - keyring->payload.subscriptions = klist; + rcu_read_unlock(); + + rcu_assign_pointer(keyring->payload.subscriptions, klist); ret = 0; } @@ -192,7 +205,7 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } - klist = keyring->payload.subscriptions; + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { for (loop = klist->nkeys - 1; loop >= 0; loop--) key_put(klist->keys[loop]); @@ -216,17 +229,20 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) seq_puts(m, "[anon]"); } - klist = keyring->payload.subscriptions; + rcu_read_lock(); + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); else seq_puts(m, ": empty"); + rcu_read_unlock(); } /* end keyring_describe() */ /*****************************************************************************/ /* * read a list of key IDs from the keyring's contents + * - the keyring's semaphore is read-locked */ static long keyring_read(const struct key *keyring, char __user *buffer, size_t buflen) @@ -237,7 +253,7 @@ static long keyring_read(const struct key *keyring, int loop, ret; ret = 0; - klist = keyring->payload.subscriptions; + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { /* calculate how much data we could return */ @@ -292,7 +308,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, uid, gid, KEY_USR_ALL, not_in_quota); if (!IS_ERR(keyring)) { - ret = key_instantiate_and_link(keyring, NULL, 0, dest); + ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); if (ret < 0) { key_put(keyring); keyring = ERR_PTR(ret); @@ -310,17 +326,18 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, * - we only find keys on which we have search permission * - we use the supplied match function to see if the description (or other * feature of interest) matches - * - we readlock the keyrings as we search down the tree + * - we rely on RCU to prevent the keyring lists from disappearing on us * - we return -EAGAIN if we didn't find any matching key * - we return -ENOKEY if we only found negative matching keys */ struct key *keyring_search_aux(struct key *keyring, + struct task_struct *context, struct key_type *type, const void *description, key_match_func_t match) { struct { - struct key *keyring; + struct keyring_list *keylist; int kix; } stack[KEYRING_SEARCH_MAX_DEPTH]; @@ -328,13 +345,15 @@ struct key *keyring_search_aux(struct key *keyring, struct timespec now; struct key *key; long err; - int sp, psp, kix; + int sp, kix; key_check(keyring); + rcu_read_lock(); + /* top keyring must have search permission to begin the search */ key = ERR_PTR(-EACCES); - if (!key_permission(keyring, KEY_SEARCH)) + if (!key_task_permission(keyring, context, KEY_SEARCH)) goto error; key = ERR_PTR(-ENOTDIR); @@ -347,11 +366,10 @@ struct key *keyring_search_aux(struct key *keyring, /* start processing a new keyring */ descend: - read_lock(&keyring->lock); - if (keyring->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) goto not_this_keyring; - keylist = keyring->payload.subscriptions; + keylist = rcu_dereference(keyring->payload.subscriptions); if (!keylist) goto not_this_keyring; @@ -364,7 +382,7 @@ struct key *keyring_search_aux(struct key *keyring, continue; /* skip revoked keys and expired keys */ - if (key->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &key->flags)) continue; if (key->expiry && now.tv_sec >= key->expiry) @@ -375,11 +393,11 @@ struct key *keyring_search_aux(struct key *keyring, continue; /* key must have search permissions */ - if (!key_permission(key, KEY_SEARCH)) + if (!key_task_permission(key, context, KEY_SEARCH)) continue; /* we set a different error code if we find a negative key */ - if (key->flags & KEY_FLAG_NEGATIVE) { + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { err = -ENOKEY; continue; } @@ -390,48 +408,37 @@ struct key *keyring_search_aux(struct key *keyring, /* search through the keyrings nested in this one */ kix = 0; ascend: - while (kix < keylist->nkeys) { + for (; kix < keylist->nkeys; kix++) { key = keylist->keys[kix]; if (key->type != &key_type_keyring) - goto next; + continue; /* recursively search nested keyrings * - only search keyrings for which we have search permission */ if (sp >= KEYRING_SEARCH_MAX_DEPTH) - goto next; + continue; - if (!key_permission(key, KEY_SEARCH)) - goto next; - - /* evade loops in the keyring tree */ - for (psp = 0; psp < sp; psp++) - if (stack[psp].keyring == keyring) - goto next; + if (!key_task_permission(key, context, KEY_SEARCH)) + continue; /* stack the current position */ - stack[sp].keyring = keyring; + stack[sp].keylist = keylist; stack[sp].kix = kix; sp++; /* begin again with the new keyring */ keyring = key; goto descend; - - next: - kix++; } /* the keyring we're looking at was disqualified or didn't contain a * matching key */ not_this_keyring: - read_unlock(&keyring->lock); - if (sp > 0) { /* resume the processing of a keyring higher up in the tree */ sp--; - keyring = stack[sp].keyring; - keylist = keyring->payload.subscriptions; + keylist = stack[sp].keylist; kix = stack[sp].kix + 1; goto ascend; } @@ -442,16 +449,9 @@ struct key *keyring_search_aux(struct key *keyring, /* we found a viable match */ found: atomic_inc(&key->usage); - read_unlock(&keyring->lock); - - /* unwind the keyring stack */ - while (sp > 0) { - sp--; - read_unlock(&stack[sp].keyring->lock); - } - key_check(key); error: + rcu_read_unlock(); return key; } /* end keyring_search_aux() */ @@ -469,7 +469,11 @@ struct key *keyring_search(struct key *keyring, struct key_type *type, const char *description) { - return keyring_search_aux(keyring, type, description, type->match); + if (!type->match) + return ERR_PTR(-ENOKEY); + + return keyring_search_aux(keyring, current, + type, description, type->match); } /* end keyring_search() */ @@ -489,15 +493,18 @@ struct key *__keyring_search_one(struct key *keyring, struct key *key; int loop; - klist = keyring->payload.subscriptions; + rcu_read_lock(); + + klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { for (loop = 0; loop < klist->nkeys; loop++) { key = klist->keys[loop]; if (key->type == ktype && - key->type->match(key, description) && + (!key->type->match || + key->type->match(key, description)) && key_permission(key, perm) && - !(key->flags & KEY_FLAG_REVOKED) + !test_bit(KEY_FLAG_REVOKED, &key->flags) ) goto found; } @@ -509,10 +516,56 @@ struct key *__keyring_search_one(struct key *keyring, found: atomic_inc(&key->usage); error: + rcu_read_unlock(); return key; } /* end __keyring_search_one() */ +/*****************************************************************************/ +/* + * search for an instantiation authorisation key matching a target key + * - the RCU read lock must be held by the caller + * - a target_id of zero specifies any valid token + */ +struct key *keyring_search_instkey(struct key *keyring, + key_serial_t target_id) +{ + struct request_key_auth *rka; + struct keyring_list *klist; + struct key *instkey; + int loop; + + klist = rcu_dereference(keyring->payload.subscriptions); + if (klist) { + for (loop = 0; loop < klist->nkeys; loop++) { + instkey = klist->keys[loop]; + + if (instkey->type != &key_type_request_key_auth) + continue; + + rka = instkey->payload.data; + if (target_id && rka->target_key->serial != target_id) + continue; + + /* the auth key is revoked during instantiation */ + if (!test_bit(KEY_FLAG_REVOKED, &instkey->flags)) + goto found; + + instkey = ERR_PTR(-EKEYREVOKED); + goto error; + } + } + + instkey = ERR_PTR(-EACCES); + goto error; + +found: + atomic_inc(&instkey->usage); +error: + return instkey; + +} /* end keyring_search_instkey() */ + /*****************************************************************************/ /* * find a keyring with the specified name @@ -540,7 +593,7 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound) &keyring_name_hash[bucket], type_data.link ) { - if (keyring->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) continue; if (strcmp(keyring->description, name) != 0) @@ -579,7 +632,7 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound) static int keyring_detect_cycle(struct key *A, struct key *B) { struct { - struct key *subtree; + struct keyring_list *keylist; int kix; } stack[KEYRING_SEARCH_MAX_DEPTH]; @@ -587,20 +640,21 @@ static int keyring_detect_cycle(struct key *A, struct key *B) struct key *subtree, *key; int sp, kix, ret; + rcu_read_lock(); + ret = -EDEADLK; if (A == B) - goto error; + goto cycle_detected; subtree = B; sp = 0; /* start processing a new keyring */ descend: - read_lock(&subtree->lock); - if (subtree->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &subtree->flags)) goto not_this_keyring; - keylist = subtree->payload.subscriptions; + keylist = rcu_dereference(subtree->payload.subscriptions); if (!keylist) goto not_this_keyring; kix = 0; @@ -619,7 +673,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B) goto too_deep; /* stack the current position */ - stack[sp].subtree = subtree; + stack[sp].keylist = keylist; stack[sp].kix = kix; sp++; @@ -632,13 +686,10 @@ static int keyring_detect_cycle(struct key *A, struct key *B) /* the keyring we're looking at was disqualified or didn't contain a * matching key */ not_this_keyring: - read_unlock(&subtree->lock); - if (sp > 0) { /* resume the checking of a keyring higher up in the tree */ sp--; - subtree = stack[sp].subtree; - keylist = subtree->payload.subscriptions; + keylist = stack[sp].keylist; kix = stack[sp].kix + 1; goto ascend; } @@ -646,30 +697,36 @@ static int keyring_detect_cycle(struct key *A, struct key *B) ret = 0; /* no cycles detected */ error: + rcu_read_unlock(); return ret; too_deep: ret = -ELOOP; - goto error_unwind; + goto error; + cycle_detected: ret = -EDEADLK; - error_unwind: - read_unlock(&subtree->lock); - - /* unwind the keyring stack */ - while (sp > 0) { - sp--; - read_unlock(&stack[sp].subtree->lock); - } - goto error; } /* end keyring_detect_cycle() */ +/*****************************************************************************/ +/* + * dispose of a keyring list after the RCU grace period + */ +static void keyring_link_rcu_disposal(struct rcu_head *rcu) +{ + struct keyring_list *klist = + container_of(rcu, struct keyring_list, rcu); + + kfree(klist); + +} /* end keyring_link_rcu_disposal() */ + /*****************************************************************************/ /* * link a key into to a keyring - * - must be called with the keyring's semaphore held + * - must be called with the keyring's semaphore write-locked */ int __key_link(struct key *keyring, struct key *key) { @@ -679,7 +736,7 @@ int __key_link(struct key *keyring, struct key *key) int ret; ret = -EKEYREVOKED; - if (keyring->flags & KEY_FLAG_REVOKED) + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) goto error; ret = -ENOTDIR; @@ -710,9 +767,10 @@ int __key_link(struct key *keyring, struct key *key) /* there's sufficient slack space to add directly */ atomic_inc(&key->usage); - write_lock(&keyring->lock); - klist->keys[klist->nkeys++] = key; - write_unlock(&keyring->lock); + klist->keys[klist->nkeys] = key; + smp_wmb(); + klist->nkeys++; + smp_wmb(); ret = 0; } @@ -723,6 +781,8 @@ int __key_link(struct key *keyring, struct key *key) max += klist->maxkeys; ret = -ENFILE; + if (max > 65535) + goto error3; size = sizeof(*klist) + sizeof(*key) * max; if (size > PAGE_SIZE) goto error3; @@ -743,14 +803,13 @@ int __key_link(struct key *keyring, struct key *key) /* add the key into the new space */ atomic_inc(&key->usage); - - write_lock(&keyring->lock); - keyring->payload.subscriptions = nklist; nklist->keys[nklist->nkeys++] = key; - write_unlock(&keyring->lock); + + rcu_assign_pointer(keyring->payload.subscriptions, nklist); /* dispose of the old keyring list */ - kfree(klist); + if (klist) + call_rcu(&klist->rcu, keyring_link_rcu_disposal); ret = 0; } @@ -789,13 +848,28 @@ int key_link(struct key *keyring, struct key *key) EXPORT_SYMBOL(key_link); +/*****************************************************************************/ +/* + * dispose of a keyring list after the RCU grace period, freeing the unlinked + * key + */ +static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) +{ + struct keyring_list *klist = + container_of(rcu, struct keyring_list, rcu); + + key_put(klist->keys[klist->delkey]); + kfree(klist); + +} /* end keyring_unlink_rcu_disposal() */ + /*****************************************************************************/ /* * unlink the first link to a key from a keyring */ int key_unlink(struct key *keyring, struct key *key) { - struct keyring_list *klist; + struct keyring_list *klist, *nklist; int loop, ret; key_check(keyring); @@ -819,36 +893,69 @@ int key_unlink(struct key *keyring, struct key *key) ret = -ENOENT; goto error; - key_is_present: +key_is_present: + /* we need to copy the key list for RCU purposes */ + nklist = kmalloc(sizeof(*klist) + sizeof(*key) * klist->maxkeys, + GFP_KERNEL); + if (!nklist) + goto nomem; + nklist->maxkeys = klist->maxkeys; + nklist->nkeys = klist->nkeys - 1; + + if (loop > 0) + memcpy(&nklist->keys[0], + &klist->keys[0], + loop * sizeof(klist->keys[0])); + + if (loop < nklist->nkeys) + memcpy(&nklist->keys[loop], + &klist->keys[loop + 1], + (nklist->nkeys - loop) * sizeof(klist->keys[0])); + /* adjust the user's quota */ key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); - /* shuffle down the key pointers - * - it might be worth shrinking the allocated memory, but that runs - * the risk of ENOMEM as we would have to copy - */ - write_lock(&keyring->lock); - - klist->nkeys--; - if (loop < klist->nkeys) - memcpy(&klist->keys[loop], - &klist->keys[loop + 1], - (klist->nkeys - loop) * sizeof(struct key *)); - - write_unlock(&keyring->lock); + rcu_assign_pointer(keyring->payload.subscriptions, nklist); up_write(&keyring->sem); - key_put(key); + + /* schedule for later cleanup */ + klist->delkey = loop; + call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); + ret = 0; - error: +error: return ret; +nomem: + ret = -ENOMEM; + up_write(&keyring->sem); + goto error; } /* end key_unlink() */ EXPORT_SYMBOL(key_unlink); +/*****************************************************************************/ +/* + * dispose of a keyring list after the RCU grace period, releasing the keys it + * links to + */ +static void keyring_clear_rcu_disposal(struct rcu_head *rcu) +{ + struct keyring_list *klist; + int loop; + + klist = container_of(rcu, struct keyring_list, rcu); + + for (loop = klist->nkeys - 1; loop >= 0; loop--) + key_put(klist->keys[loop]); + + kfree(klist); + +} /* end keyring_clear_rcu_disposal() */ + /*****************************************************************************/ /* * clear the specified process keyring @@ -857,7 +964,7 @@ EXPORT_SYMBOL(key_unlink); int keyring_clear(struct key *keyring) { struct keyring_list *klist; - int loop, ret; + int ret; ret = -ENOTDIR; if (keyring->type == &key_type_keyring) { @@ -870,20 +977,15 @@ int keyring_clear(struct key *keyring) key_payload_reserve(keyring, sizeof(struct keyring_list)); - write_lock(&keyring->lock); - keyring->payload.subscriptions = NULL; - write_unlock(&keyring->lock); + rcu_assign_pointer(keyring->payload.subscriptions, + NULL); } up_write(&keyring->sem); /* free the keys after the locks have been dropped */ - if (klist) { - for (loop = klist->nkeys - 1; loop >= 0; loop--) - key_put(klist->keys[loop]); - - kfree(klist); - } + if (klist) + call_rcu(&klist->rcu, keyring_clear_rcu_disposal); ret = 0; } diff --git a/security/keys/proc.c b/security/keys/proc.c index 91343b85c39c..c55cf1fd0826 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -140,7 +140,7 @@ static int proc_keys_show(struct seq_file *m, void *v) now = current_kernel_time(); - read_lock(&key->lock); + rcu_read_lock(); /* come up with a suitable timeout value */ if (key->expiry == 0) { @@ -164,14 +164,17 @@ static int proc_keys_show(struct seq_file *m, void *v) sprintf(xbuf, "%luw", timo / (60*60*24*7)); } +#define showflag(KEY, LETTER, FLAG) \ + (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') + seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ", key->serial, - key->flags & KEY_FLAG_INSTANTIATED ? 'I' : '-', - key->flags & KEY_FLAG_REVOKED ? 'R' : '-', - key->flags & KEY_FLAG_DEAD ? 'D' : '-', - key->flags & KEY_FLAG_IN_QUOTA ? 'Q' : '-', - key->flags & KEY_FLAG_USER_CONSTRUCT ? 'U' : '-', - key->flags & KEY_FLAG_NEGATIVE ? 'N' : '-', + showflag(key, 'I', KEY_FLAG_INSTANTIATED), + showflag(key, 'R', KEY_FLAG_REVOKED), + showflag(key, 'D', KEY_FLAG_DEAD), + showflag(key, 'Q', KEY_FLAG_IN_QUOTA), + showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), + showflag(key, 'N', KEY_FLAG_NEGATIVE), atomic_read(&key->usage), xbuf, key->perm, @@ -179,11 +182,13 @@ static int proc_keys_show(struct seq_file *m, void *v) key->gid, key->type->name); +#undef showflag + if (key->type->describe) key->type->describe(key, m); seq_putc(m, '\n'); - read_unlock(&key->lock); + rcu_read_unlock(); return 0; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 2eb0e471cd40..34db087bbcc7 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -1,6 +1,6 @@ /* process_keys.c: management of a process's keyrings * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -38,10 +38,9 @@ struct key root_user_keyring = { .serial = 2, .type = &key_type_keyring, .user = &root_key_user, - .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), .perm = KEY_USR_ALL, - .flags = KEY_FLAG_INSTANTIATED, + .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid.0", #ifdef KEY_DEBUGGING .magic = KEY_DEBUG_MAGIC, @@ -54,10 +53,9 @@ struct key root_session_keyring = { .serial = 1, .type = &key_type_keyring, .user = &root_key_user, - .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), .perm = KEY_USR_ALL, - .flags = KEY_FLAG_INSTANTIATED, + .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid_ses.0", #ifdef KEY_DEBUGGING .magic = KEY_DEBUG_MAGIC, @@ -167,7 +165,7 @@ int install_thread_keyring(struct task_struct *tsk) /* * make sure a process keyring is installed */ -static int install_process_keyring(struct task_struct *tsk) +int install_process_keyring(struct task_struct *tsk) { unsigned long flags; struct key *keyring; @@ -183,7 +181,7 @@ static int install_process_keyring(struct task_struct *tsk) goto error; } - /* attach or swap keyrings */ + /* attach keyring */ spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->process_keyring) { tsk->signal->process_keyring = keyring; @@ -229,12 +227,14 @@ static int install_session_keyring(struct task_struct *tsk, /* install the keyring */ spin_lock_irqsave(&tsk->sighand->siglock, flags); - old = tsk->signal->session_keyring; - tsk->signal->session_keyring = keyring; + old = rcu_dereference(tsk->signal->session_keyring); + rcu_assign_pointer(tsk->signal->session_keyring, keyring); spin_unlock_irqrestore(&tsk->sighand->siglock, flags); ret = 0; + /* we're using RCU on the pointer */ + synchronize_kernel(); key_put(old); error: return ret; @@ -247,8 +247,6 @@ static int install_session_keyring(struct task_struct *tsk, */ int copy_thread_group_keys(struct task_struct *tsk) { - unsigned long flags; - key_check(current->thread_group->session_keyring); key_check(current->thread_group->process_keyring); @@ -256,10 +254,10 @@ int copy_thread_group_keys(struct task_struct *tsk) tsk->signal->process_keyring = NULL; /* same session keyring */ - spin_lock_irqsave(¤t->sighand->siglock, flags); + rcu_read_lock(); tsk->signal->session_keyring = - key_get(current->signal->session_keyring); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + key_get(rcu_dereference(current->signal->session_keyring)); + rcu_read_unlock(); return 0; @@ -349,9 +347,7 @@ void key_fsuid_changed(struct task_struct *tsk) /* update the ownership of the thread keyring */ if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); - write_lock(&tsk->thread_keyring->lock); tsk->thread_keyring->uid = tsk->fsuid; - write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } @@ -366,9 +362,7 @@ void key_fsgid_changed(struct task_struct *tsk) /* update the ownership of the thread keyring */ if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); - write_lock(&tsk->thread_keyring->lock); tsk->thread_keyring->gid = tsk->fsgid; - write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } @@ -382,13 +376,13 @@ void key_fsgid_changed(struct task_struct *tsk) * - we return -EAGAIN if we didn't find any matching key * - we return -ENOKEY if we found only negative matching keys */ -struct key *search_process_keyrings_aux(struct key_type *type, - const void *description, - key_match_func_t match) +struct key *search_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + struct task_struct *context) { - struct task_struct *tsk = current; - unsigned long flags; - struct key *key, *ret, *err, *tmp; + struct request_key_auth *rka; + struct key *key, *ret, *err, *instkey; /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; @@ -402,9 +396,9 @@ struct key *search_process_keyrings_aux(struct key_type *type, err = ERR_PTR(-EAGAIN); /* search the thread keyring first */ - if (tsk->thread_keyring) { - key = keyring_search_aux(tsk->thread_keyring, type, - description, match); + if (context->thread_keyring) { + key = keyring_search_aux(context->thread_keyring, + context, type, description, match); if (!IS_ERR(key)) goto found; @@ -422,9 +416,9 @@ struct key *search_process_keyrings_aux(struct key_type *type, } /* search the process keyring second */ - if (tsk->signal->process_keyring) { - key = keyring_search_aux(tsk->signal->process_keyring, - type, description, match); + if (context->signal->process_keyring) { + key = keyring_search_aux(context->signal->process_keyring, + context, type, description, match); if (!IS_ERR(key)) goto found; @@ -441,52 +435,93 @@ struct key *search_process_keyrings_aux(struct key_type *type, } } - /* search the session keyring last */ - spin_lock_irqsave(&tsk->sighand->siglock, flags); + /* search the session keyring */ + if (context->signal->session_keyring) { + rcu_read_lock(); + key = keyring_search_aux( + rcu_dereference(context->signal->session_keyring), + context, type, description, match); + rcu_read_unlock(); - tmp = tsk->signal->session_keyring; - if (!tmp) - tmp = tsk->user->session_keyring; - atomic_inc(&tmp->usage); + if (!IS_ERR(key)) + goto found; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); - - key = keyring_search_aux(tmp, type, description, match); - key_put(tmp); - if (!IS_ERR(key)) - goto found; - - switch (PTR_ERR(key)) { - case -EAGAIN: /* no key */ - if (ret) + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; break; - case -ENOKEY: /* negative key */ - ret = key; - break; - default: - err = key; - break; + default: + err = key; + break; + } + + /* if this process has a session keyring and that has an + * instantiation authorisation key in the bottom level, then we + * also search the keyrings of the process mentioned there */ + if (context != current) + goto no_key; + + rcu_read_lock(); + instkey = __keyring_search_one( + rcu_dereference(context->signal->session_keyring), + &key_type_request_key_auth, NULL, 0); + rcu_read_unlock(); + + if (IS_ERR(instkey)) + goto no_key; + + rka = instkey->payload.data; + + key = search_process_keyrings(type, description, match, + rka->context); + key_put(instkey); + + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } + } + /* or search the user-session keyring */ + else { + key = keyring_search_aux(context->user->session_keyring, + context, type, description, match); + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } } + +no_key: /* no key - decide on the error we're going to go for */ key = ret ? ret : err; - found: +found: return key; -} /* end search_process_keyrings_aux() */ - -/*****************************************************************************/ -/* - * search the process keyrings for the first matching key - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys - */ -struct key *search_process_keyrings(struct key_type *type, - const char *description) -{ - return search_process_keyrings_aux(type, description, type->match); - } /* end search_process_keyrings() */ /*****************************************************************************/ @@ -495,72 +530,73 @@ struct key *search_process_keyrings(struct key_type *type, * - don't create special keyrings unless so requested * - partially constructed keys aren't found unless requested */ -struct key *lookup_user_key(key_serial_t id, int create, int partial, - key_perm_t perm) +struct key *lookup_user_key(struct task_struct *context, key_serial_t id, + int create, int partial, key_perm_t perm) { - struct task_struct *tsk = current; - unsigned long flags; struct key *key; int ret; + if (!context) + context = current; + key = ERR_PTR(-ENOKEY); switch (id) { case KEY_SPEC_THREAD_KEYRING: - if (!tsk->thread_keyring) { + if (!context->thread_keyring) { if (!create) goto error; - ret = install_thread_keyring(tsk); + ret = install_thread_keyring(context); if (ret < 0) { key = ERR_PTR(ret); goto error; } } - key = tsk->thread_keyring; + key = context->thread_keyring; atomic_inc(&key->usage); break; case KEY_SPEC_PROCESS_KEYRING: - if (!tsk->signal->process_keyring) { + if (!context->signal->process_keyring) { if (!create) goto error; - ret = install_process_keyring(tsk); + ret = install_process_keyring(context); if (ret < 0) { key = ERR_PTR(ret); goto error; } } - key = tsk->signal->process_keyring; + key = context->signal->process_keyring; atomic_inc(&key->usage); break; case KEY_SPEC_SESSION_KEYRING: - if (!tsk->signal->session_keyring) { + if (!context->signal->session_keyring) { /* always install a session keyring upon access if one * doesn't exist yet */ ret = install_session_keyring( - tsk, tsk->user->session_keyring); + context, context->user->session_keyring); if (ret < 0) goto error; } - spin_lock_irqsave(&tsk->sighand->siglock, flags); - key = tsk->signal->session_keyring; + rcu_read_lock(); + key = rcu_dereference(context->signal->session_keyring); atomic_inc(&key->usage); - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_read_unlock(); break; case KEY_SPEC_USER_KEYRING: - key = tsk->user->uid_keyring; + key = context->user->uid_keyring; atomic_inc(&key->usage); break; case KEY_SPEC_USER_SESSION_KEYRING: - key = tsk->user->session_keyring; + key = context->user->session_keyring; atomic_inc(&key->usage); break; @@ -580,7 +616,7 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, break; } - /* check the status and permissions */ + /* check the status */ if (perm) { ret = key_validate(key); if (ret < 0) @@ -588,11 +624,13 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, } ret = -EIO; - if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED)) + if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto invalid_key; + /* check the permissions */ ret = -EACCES; - if (!key_permission(key, perm)) + + if (!key_task_permission(key, context, perm)) goto invalid_key; error: @@ -615,7 +653,6 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, long join_session_keyring(const char *name) { struct task_struct *tsk = current; - unsigned long flags; struct key *keyring; long ret; @@ -625,9 +662,9 @@ long join_session_keyring(const char *name) if (ret < 0) goto error; - spin_lock_irqsave(&tsk->sighand->siglock, flags); - ret = tsk->signal->session_keyring->serial; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_read_lock(); + ret = rcu_dereference(tsk->signal->session_keyring)->serial; + rcu_read_unlock(); goto error; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 9705b1aeba5d..dfcd983af1fd 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -1,6 +1,6 @@ /* request_key.c: request a key from userspace * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -13,6 +13,7 @@ #include #include #include +#include #include "internal.h" struct key_construction { @@ -27,18 +28,26 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); /* * request userspace finish the construction of a key * - execute "/sbin/request-key " - * - if callout_info is an empty string, it'll be rendered as a "-" instead */ static int call_request_key(struct key *key, const char *op, const char *callout_info) { struct task_struct *tsk = current; - unsigned long flags; key_serial_t prkey, sskey; + struct key *session_keyring, *rkakey; char *argv[10], *envp[3], uid_str[12], gid_str[12]; char key_str[12], keyring_str[3][12]; - int i; + int ret, i; + + kenter("{%d},%s,%s", key->serial, op, callout_info); + + /* generate a new session keyring with an auth key in it */ + session_keyring = request_key_auth_new(key, &rkakey); + if (IS_ERR(session_keyring)) { + ret = PTR_ERR(session_keyring); + goto error; + } /* record the UID and GID */ sprintf(uid_str, "%d", current->fsuid); @@ -55,17 +64,17 @@ static int call_request_key(struct key *key, if (tsk->signal->process_keyring) prkey = tsk->signal->process_keyring->serial; - sskey = 0; - spin_lock_irqsave(&tsk->sighand->siglock, flags); - if (tsk->signal->session_keyring) - sskey = tsk->signal->session_keyring->serial; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); - - - if (!sskey) - sskey = tsk->user->session_keyring->serial; - sprintf(keyring_str[1], "%d", prkey); + + if (tsk->signal->session_keyring) { + rcu_read_lock(); + sskey = rcu_dereference(tsk->signal->session_keyring)->serial; + rcu_read_unlock(); + } + else { + sskey = tsk->user->session_keyring->serial; + } + sprintf(keyring_str[2], "%d", sskey); /* set up a minimal environment */ @@ -84,11 +93,20 @@ static int call_request_key(struct key *key, argv[i++] = keyring_str[0]; argv[i++] = keyring_str[1]; argv[i++] = keyring_str[2]; - argv[i++] = callout_info[0] ? (char *) callout_info : "-"; + argv[i++] = (char *) callout_info; argv[i] = NULL; /* do it */ - return call_usermodehelper(argv[0], argv, envp, 1); + ret = call_usermodehelper_keys(argv[0], argv, envp, session_keyring, 1); + + /* dispose of the special keys */ + key_revoke(rkakey); + key_put(rkakey); + key_put(session_keyring); + + error: + kleave(" = %d", ret); + return ret; } /* end call_request_key() */ @@ -105,7 +123,9 @@ static struct key *__request_key_construction(struct key_type *type, struct key_construction cons; struct timespec now; struct key *key; - int ret, negative; + int ret, negated; + + kenter("%s,%s,%s", type->name, description, callout_info); /* create a key and add it to the queue */ key = key_alloc(type, description, @@ -113,9 +133,7 @@ static struct key *__request_key_construction(struct key_type *type, if (IS_ERR(key)) goto alloc_failed; - write_lock(&key->lock); - key->flags |= KEY_FLAG_USER_CONSTRUCT; - write_unlock(&key->lock); + set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); cons.key = key; list_add_tail(&cons.link, &key->user->consq); @@ -130,7 +148,7 @@ static struct key *__request_key_construction(struct key_type *type, /* if the key wasn't instantiated, then we want to give an error */ ret = -ENOKEY; - if (!(key->flags & KEY_FLAG_INSTANTIATED)) + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto request_failed; down_write(&key_construction_sem); @@ -139,12 +157,13 @@ static struct key *__request_key_construction(struct key_type *type, /* also give an error if the key was negatively instantiated */ check_not_negative: - if (key->flags & KEY_FLAG_NEGATIVE) { + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { key_put(key); key = ERR_PTR(-ENOKEY); } out: + kleave(" = %p", key); return key; request_failed: @@ -152,24 +171,23 @@ static struct key *__request_key_construction(struct key_type *type, * - remove from construction queue * - mark the key as dead */ - negative = 0; + negated = 0; down_write(&key_construction_sem); list_del(&cons.link); - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; - /* check it didn't get instantiated between the check and the down */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { - key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; - negative = 1; + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { + set_bit(KEY_FLAG_NEGATIVE, &key->flags); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); + negated = 1; } - write_unlock(&key->lock); + clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); + up_write(&key_construction_sem); - if (!negative) + if (!negated) goto check_not_negative; /* surprisingly, the key got * instantiated */ @@ -178,13 +196,12 @@ static struct key *__request_key_construction(struct key_type *type, key->expiry = now.tv_sec + key_negative_timeout; if (current->signal->session_keyring) { - unsigned long flags; struct key *keyring; - spin_lock_irqsave(¤t->sighand->siglock, flags); - keyring = current->signal->session_keyring; + rcu_read_lock(); + keyring = rcu_dereference(current->signal->session_keyring); atomic_inc(&keyring->usage); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + rcu_read_unlock(); key_link(keyring, key); key_put(keyring); @@ -220,6 +237,9 @@ static struct key *request_key_construction(struct key_type *type, DECLARE_WAITQUEUE(myself, current); + kenter("%s,%s,{%d},%s", + type->name, description, user->uid, callout_info); + /* see if there's such a key under construction already */ down_write(&key_construction_sem); @@ -236,6 +256,7 @@ static struct key *request_key_construction(struct key_type *type, /* see about getting userspace to construct the key */ key = __request_key_construction(type, description, callout_info); error: + kleave(" = %p", key); return key; /* someone else has the same key under construction @@ -249,8 +270,10 @@ static struct key *request_key_construction(struct key_type *type, add_wait_queue(&request_key_conswq, &myself); for (;;) { - set_current_state(TASK_UNINTERRUPTIBLE); - if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT)) + set_current_state(TASK_INTERRUPTIBLE); + if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags)) + break; + if (signal_pending(current)) break; schedule(); } @@ -269,23 +292,85 @@ static struct key *request_key_construction(struct key_type *type, } /* end request_key_construction() */ +/*****************************************************************************/ +/* + * link a freshly minted key to an appropriate destination keyring + */ +static void request_key_link(struct key *key, struct key *dest_keyring) +{ + struct task_struct *tsk = current; + struct key *drop = NULL; + + kenter("{%d},%p", key->serial, dest_keyring); + + /* find the appropriate keyring */ + if (!dest_keyring) { + switch (tsk->jit_keyring) { + case KEY_REQKEY_DEFL_DEFAULT: + case KEY_REQKEY_DEFL_THREAD_KEYRING: + dest_keyring = tsk->thread_keyring; + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_PROCESS_KEYRING: + dest_keyring = tsk->signal->process_keyring; + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_SESSION_KEYRING: + rcu_read_lock(); + dest_keyring = key_get( + rcu_dereference(tsk->signal->session_keyring)); + rcu_read_unlock(); + drop = dest_keyring; + + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: + dest_keyring = current->user->session_keyring; + break; + + case KEY_REQKEY_DEFL_USER_KEYRING: + dest_keyring = current->user->uid_keyring; + break; + + case KEY_REQKEY_DEFL_GROUP_KEYRING: + default: + BUG(); + } + } + + /* and attach the key to it */ + key_link(dest_keyring, key); + + key_put(drop); + + kleave(""); + +} /* end request_key_link() */ + /*****************************************************************************/ /* * request a key * - search the process's keyrings * - check the list of keys being created or updated - * - call out to userspace for a key if requested (supplementary info can be - * passed) + * - call out to userspace for a key if supplementary info was provided + * - cache the key in an appropriate keyring */ -struct key *request_key(struct key_type *type, - const char *description, - const char *callout_info) +struct key *request_key_and_link(struct key_type *type, + const char *description, + const char *callout_info, + struct key *dest_keyring) { struct key_user *user; struct key *key; + kenter("%s,%s,%s,%p", + type->name, description, callout_info, dest_keyring); + /* search all the process keyrings for a key */ - key = search_process_keyrings_aux(type, description, type->match); + key = search_process_keyrings(type, description, type->match, current); if (PTR_ERR(key) == -EAGAIN) { /* the search failed, but the keyrings were searchable, so we @@ -296,12 +381,13 @@ struct key *request_key(struct key_type *type, /* - get hold of the user's construction queue */ user = key_user_lookup(current->fsuid); - if (!user) { - key = ERR_PTR(-ENOMEM); - goto error; - } + if (!user) + goto nomem; + + do { + if (signal_pending(current)) + goto interrupted; - for (;;) { /* ask userspace (returns NULL if it waited on a key * being constructed) */ key = request_key_construction(type, description, @@ -311,18 +397,46 @@ struct key *request_key(struct key_type *type, /* someone else made the key we want, so we need to * search again as it might now be available to us */ - key = search_process_keyrings_aux(type, description, - type->match); - if (PTR_ERR(key) != -EAGAIN) - break; - } + key = search_process_keyrings(type, description, + type->match, current); + + } while (PTR_ERR(key) == -EAGAIN); key_user_put(user); + + /* link the new key into the appropriate keyring */ + if (!PTR_ERR(key)) + request_key_link(key, dest_keyring); } - error: +error: + kleave(" = %p", key); return key; +nomem: + key = ERR_PTR(-ENOMEM); + goto error; + +interrupted: + key_user_put(user); + key = ERR_PTR(-EINTR); + goto error; + +} /* end request_key_and_link() */ + +/*****************************************************************************/ +/* + * request a key + * - search the process's keyrings + * - check the list of keys being created or updated + * - call out to userspace for a key if supplementary info was provided + */ +struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info) +{ + return request_key_and_link(type, description, callout_info, NULL); + } /* end request_key() */ EXPORT_SYMBOL(request_key); @@ -339,7 +453,8 @@ int key_validate(struct key *key) if (key) { /* check it's still accessible */ ret = -EKEYREVOKED; - if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD)) + if (test_bit(KEY_FLAG_REVOKED, &key->flags) || + test_bit(KEY_FLAG_DEAD, &key->flags)) goto error; /* check it hasn't expired */ diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c new file mode 100644 index 000000000000..f22264632229 --- /dev/null +++ b/security/keys/request_key_auth.c @@ -0,0 +1,180 @@ +/* request_key_auth.c: request key authorisation controlling key def + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@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. + */ + +#include +#include +#include +#include +#include "internal.h" + +static int request_key_auth_instantiate(struct key *, const void *, size_t); +static void request_key_auth_describe(const struct key *, struct seq_file *); +static void request_key_auth_destroy(struct key *); + +/* + * the request-key authorisation key type definition + */ +struct key_type key_type_request_key_auth = { + .name = ".request_key_auth", + .def_datalen = sizeof(struct request_key_auth), + .instantiate = request_key_auth_instantiate, + .describe = request_key_auth_describe, + .destroy = request_key_auth_destroy, +}; + +/*****************************************************************************/ +/* + * instantiate a request-key authorisation record + */ +static int request_key_auth_instantiate(struct key *key, + const void *data, + size_t datalen) +{ + struct request_key_auth *rka, *irka; + struct key *instkey; + int ret; + + ret = -ENOMEM; + rka = kmalloc(sizeof(*rka), GFP_KERNEL); + if (rka) { + /* see if the calling process is already servicing the key + * request of another process */ + instkey = key_get_instantiation_authkey(0); + if (!IS_ERR(instkey)) { + /* it is - use that instantiation context here too */ + irka = instkey->payload.data; + rka->context = irka->context; + rka->pid = irka->pid; + key_put(instkey); + } + else { + /* it isn't - use this process as the context */ + rka->context = current; + rka->pid = current->pid; + } + + rka->target_key = key_get((struct key *) data); + key->payload.data = rka; + ret = 0; + } + + return ret; + +} /* end request_key_auth_instantiate() */ + +/*****************************************************************************/ +/* + * + */ +static void request_key_auth_describe(const struct key *key, + struct seq_file *m) +{ + struct request_key_auth *rka = key->payload.data; + + seq_puts(m, "key:"); + seq_puts(m, key->description); + seq_printf(m, " pid:%d", rka->pid); + +} /* end request_key_auth_describe() */ + +/*****************************************************************************/ +/* + * destroy an instantiation authorisation token key + */ +static void request_key_auth_destroy(struct key *key) +{ + struct request_key_auth *rka = key->payload.data; + + kenter("{%d}", key->serial); + + key_put(rka->target_key); + +} /* end request_key_auth_destroy() */ + +/*****************************************************************************/ +/* + * create a session keyring to be for the invokation of /sbin/request-key and + * stick an authorisation token in it + */ +struct key *request_key_auth_new(struct key *target, struct key **_rkakey) +{ + struct key *keyring, *rkakey = NULL; + char desc[20]; + int ret; + + kenter("%d,", target->serial); + + /* allocate a new session keyring */ + sprintf(desc, "_req.%u", target->serial); + + keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL); + if (IS_ERR(keyring)) { + kleave("= %ld", PTR_ERR(keyring)); + return keyring; + } + + /* allocate the auth key */ + sprintf(desc, "%x", target->serial); + + rkakey = key_alloc(&key_type_request_key_auth, desc, + current->fsuid, current->fsgid, + KEY_USR_VIEW, 1); + if (IS_ERR(rkakey)) { + key_put(keyring); + kleave("= %ld", PTR_ERR(rkakey)); + return rkakey; + } + + /* construct and attach to the keyring */ + ret = key_instantiate_and_link(rkakey, target, 0, keyring, NULL); + if (ret < 0) { + key_revoke(rkakey); + key_put(rkakey); + key_put(keyring); + kleave("= %d", ret); + return ERR_PTR(ret); + } + + *_rkakey = rkakey; + kleave(" = {%d} ({%d})", keyring->serial, rkakey->serial); + return keyring; + +} /* end request_key_auth_new() */ + +/*****************************************************************************/ +/* + * get the authorisation key for instantiation of a specific key if attached to + * the current process's keyrings + * - this key is inserted into a keyring and that is set as /sbin/request-key's + * session keyring + * - a target_id of zero specifies any valid token + */ +struct key *key_get_instantiation_authkey(key_serial_t target_id) +{ + struct task_struct *tsk = current; + struct key *instkey; + + /* we must have our own personal session keyring */ + if (!tsk->signal->session_keyring) + return ERR_PTR(-EACCES); + + /* and it must contain a suitable request authorisation key + * - lock RCU against session keyring changing + */ + rcu_read_lock(); + + instkey = keyring_search_instkey( + rcu_dereference(tsk->signal->session_keyring), target_id); + + rcu_read_unlock(); + return instkey; + +} /* end key_get_instantiation_authkey() */ diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8d65b3a28129..e446acba73d3 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -42,12 +42,21 @@ struct key_type key_type_user = { .read = user_read, }; +struct user_key_payload { + struct rcu_head rcu; /* RCU destructor */ + unsigned short datalen; /* length of this data */ + char data[0]; /* actual data */ +}; + +EXPORT_SYMBOL_GPL(key_type_user); + /*****************************************************************************/ /* * instantiate a user defined key */ static int user_instantiate(struct key *key, const void *data, size_t datalen) { + struct user_key_payload *upayload; int ret; ret = -EINVAL; @@ -58,13 +67,15 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) if (ret < 0) goto error; - /* attach the data */ ret = -ENOMEM; - key->payload.data = kmalloc(datalen, GFP_KERNEL); - if (!key->payload.data) + upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); + if (!upayload) goto error; - memcpy(key->payload.data, data, datalen); + /* attach the data */ + upayload->datalen = datalen; + memcpy(upayload->data, data, datalen); + rcu_assign_pointer(key->payload.data, upayload); ret = 0; error: @@ -75,18 +86,25 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) /*****************************************************************************/ /* * duplicate a user defined key + * - both keys' semaphores are locked against further modification + * - the new key cannot yet be accessed */ static int user_duplicate(struct key *key, const struct key *source) { + struct user_key_payload *upayload, *spayload; int ret; /* just copy the payload */ ret = -ENOMEM; - key->payload.data = kmalloc(source->datalen, GFP_KERNEL); + upayload = kmalloc(sizeof(*upayload) + source->datalen, GFP_KERNEL); + if (upayload) { + spayload = rcu_dereference(source->payload.data); + BUG_ON(source->datalen != spayload->datalen); - if (key->payload.data) { - key->datalen = source->datalen; - memcpy(key->payload.data, source->payload.data, source->datalen); + upayload->datalen = key->datalen = spayload->datalen; + memcpy(upayload->data, spayload->data, key->datalen); + + key->payload.data = upayload; ret = 0; } @@ -94,42 +112,56 @@ static int user_duplicate(struct key *key, const struct key *source) } /* end user_duplicate() */ +/*****************************************************************************/ +/* + * dispose of the old data from an updated user defined key + */ +static void user_update_rcu_disposal(struct rcu_head *rcu) +{ + struct user_key_payload *upayload; + + upayload = container_of(rcu, struct user_key_payload, rcu); + + kfree(upayload); + +} /* end user_update_rcu_disposal() */ + /*****************************************************************************/ /* * update a user defined key + * - the key's semaphore is write-locked */ static int user_update(struct key *key, const void *data, size_t datalen) { - void *new, *zap; + struct user_key_payload *upayload, *zap; int ret; ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !data) goto error; - /* copy the data */ + /* construct a replacement payload */ ret = -ENOMEM; - new = kmalloc(datalen, GFP_KERNEL); - if (!new) + upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); + if (!upayload) goto error; - memcpy(new, data, datalen); + upayload->datalen = datalen; + memcpy(upayload->data, data, datalen); /* check the quota and attach the new data */ - zap = new; - write_lock(&key->lock); + zap = upayload; ret = key_payload_reserve(key, datalen); if (ret == 0) { /* attach the new data, displacing the old */ zap = key->payload.data; - key->payload.data = new; + rcu_assign_pointer(key->payload.data, upayload); key->expiry = 0; } - write_unlock(&key->lock); - kfree(zap); + call_rcu(&zap->rcu, user_update_rcu_disposal); error: return ret; @@ -152,13 +184,15 @@ static int user_match(const struct key *key, const void *description) */ static void user_destroy(struct key *key) { - kfree(key->payload.data); + struct user_key_payload *upayload = key->payload.data; + + kfree(upayload); } /* end user_destroy() */ /*****************************************************************************/ /* - * describe the user + * describe the user key */ static void user_describe(const struct key *key, struct seq_file *m) { @@ -171,18 +205,23 @@ static void user_describe(const struct key *key, struct seq_file *m) /*****************************************************************************/ /* * read the key data + * - the key's semaphore is read-locked */ static long user_read(const struct key *key, char __user *buffer, size_t buflen) { - long ret = key->datalen; + struct user_key_payload *upayload; + long ret; + + upayload = rcu_dereference(key->payload.data); + ret = upayload->datalen; /* we can return the data as is */ if (buffer && buflen > 0) { - if (buflen > key->datalen) - buflen = key->datalen; + if (buflen > upayload->datalen) + buflen = upayload->datalen; - if (copy_to_user(buffer, key->payload.data, buflen) != 0) + if (copy_to_user(buffer, upayload->data, buflen) != 0) ret = -EFAULT; } diff --git a/sound/core/info.c b/sound/core/info.c index 31faffe01cb0..5e122bbe7c92 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -754,7 +755,7 @@ static snd_info_entry_t *snd_info_create_entry(const char *name) entry = kcalloc(1, sizeof(*entry), GFP_KERNEL); if (entry == NULL) return NULL; - entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); + entry->name = kstrdup(name, GFP_KERNEL); if (entry->name == NULL) { kfree(entry); return NULL; diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index f9e4ce443454..12107968d402 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,7 @@ int snd_oss_info_register(int dev, int num, char *string) x = NULL; } } else { - x = snd_kmalloc_strdup(string, GFP_KERNEL); + x = kstrdup(string, GFP_KERNEL); if (x == NULL) { up(&strings); return -ENOMEM; diff --git a/sound/core/memory.c b/sound/core/memory.c index 20860fec9364..c1fb28e84330 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -184,6 +184,20 @@ void snd_hidden_vfree(void *obj) snd_wrapper_vfree(obj); } +char *snd_hidden_kstrdup(const char *s, int flags) +{ + int len; + char *buf; + + if (!s) return NULL; + + len = strlen(s) + 1; + buf = _snd_kmalloc(len, flags); + if (buf) + memcpy(buf, s, len); + return buf; +} + static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); @@ -214,35 +228,8 @@ int __exit snd_memory_info_done(void) return 0; } -#else - -#define _snd_kmalloc kmalloc - #endif /* CONFIG_SND_DEBUG_MEMORY */ -/** - * snd_kmalloc_strdup - copy the string - * @string: the original string - * @flags: allocation conditions, GFP_XXX - * - * Allocates a memory chunk via kmalloc() and copies the string to it. - * - * Returns the pointer, or NULL if no enoguh memory. - */ -char *snd_kmalloc_strdup(const char *string, int flags) -{ - size_t len; - char *ptr; - - if (!string) - return NULL; - len = strlen(string) + 1; - ptr = _snd_kmalloc(len, flags); - if (ptr) - memcpy(ptr, string, len); - return ptr; -} - /** * copy_to_user_fromio - copy data from mmio-space to user-space * @dst: the destination pointer on user-space diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 98ed9a9f0da6..98fc0766f885 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1137,7 +1138,7 @@ static void snd_mixer_oss_proc_write(snd_info_entry_t *entry, goto __unlock; } tbl->oss_id = ch; - tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL); + tbl->name = kstrdup(str, GFP_KERNEL); if (! tbl->name) { kfree(tbl); goto __unlock; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index cab30977e7c0..de7444c586f9 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -2360,7 +2361,7 @@ static void snd_pcm_oss_proc_write(snd_info_entry_t *entry, for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next); setup1->next = setup; } - template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL); + template.task_name = kstrdup(task_name, GFP_KERNEL); } else { buffer->error = -ENOMEM; } diff --git a/sound/core/sound.c b/sound/core/sound.c index 0815fadeb3ec..7612884f530b 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -399,8 +399,8 @@ EXPORT_SYMBOL(snd_hidden_kcalloc); EXPORT_SYMBOL(snd_hidden_kfree); EXPORT_SYMBOL(snd_hidden_vmalloc); EXPORT_SYMBOL(snd_hidden_vfree); +EXPORT_SYMBOL(snd_hidden_kstrdup); #endif -EXPORT_SYMBOL(snd_kmalloc_strdup); EXPORT_SYMBOL(copy_to_user_fromio); EXPORT_SYMBOL(copy_from_user_toio); /* init.c */ diff --git a/sound/core/timer.c b/sound/core/timer.c index b498e5482d77..cfaccd415b3b 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -100,7 +101,7 @@ static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *ti timeri = kcalloc(1, sizeof(*timeri), GFP_KERNEL); if (timeri == NULL) return NULL; - timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); + timeri->owner = kstrdup(owner, GFP_KERNEL); if (! timeri->owner) { kfree(timeri); return NULL; diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index 609838e8ef67..5eb766dd564b 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -213,7 +214,7 @@ snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, if (share_id != NULL) memcpy(&block.share_id, share_id, sizeof(block.share_id)); block.owner = owner; - block.name = snd_kmalloc_strdup(name, GFP_KERNEL); + block.name = kstrdup(name, GFP_KERNEL); nblock = snd_gf1_mem_xalloc(alloc, &block); snd_gf1_mem_lock(alloc, 1); return nblock; @@ -253,13 +254,13 @@ int snd_gf1_mem_init(snd_gus_card_t * gus) if (gus->gf1.enh_mode) { block.ptr = 0; block.size = 1024; - block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL); + block.name = kstrdup("InterWave LFOs", GFP_KERNEL); if (snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; } block.ptr = gus->gf1.default_voice_address; block.size = 4; - block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL); + block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL); if (snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; #ifdef CONFIG_SND_DEBUG diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9edd558d6bd3..bab89843d850 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1781,7 +1781,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, unsign knew = &spec->kctl_alloc[spec->num_kctl_used]; *knew = alc880_control_templates[type]; - knew->name = snd_kmalloc_strdup(name, GFP_KERNEL); + knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; knew->private_value = val; diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index 16f3b461627a..60d0b2c66698 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name) snd_assert(name != NULL, return -EINVAL); emu->card = card; - emu->name = snd_kmalloc_strdup(name, GFP_KERNEL); + emu->name = kstrdup(name, GFP_KERNEL); emu->voices = kcalloc(emu->max_voices, sizeof(snd_emux_voice_t), GFP_KERNEL); if (emu->voices == NULL) return -ENOMEM;