From: Alan Cox Date: Fri, 23 Nov 2007 20:20:51 +0000 (-0500) Subject: Linux 2.2.15pre8 X-Git-Tag: 2.2.15pre8 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=e330ef6cc1bb1fc547c6d3892bf72035cda717ca;p=history.git Linux 2.2.15pre8 o Fix put_tty_char race on SMP (Andrew J Kroll) | This may well fix the serial mouse jumping problem | people have reported for a long time. o Fix compile compbination for IPv6/netlink (Arjan van de Ven) o Add Yamaha CRW6416S to the lun blacklist (Tim Waugh) o Fix symbol clash if a given pair of ISDN boards both got compiled in (Arjan van de Ven) o Hopefully fix the X.25 problem (Henner Eisen) o NCPfs permission handling updates (Petr Vandrovec) o Clean up Maestro includes (Arjan, Alan Cox) o Fix a memory leak in eql drivers (Alessandro Rubini) o Support byte counters in the tlan driver (Niels Baggesen) o Tlan fixed backported from 2.3.x (Torben Mathiasen) o Ultrasparc support for SK98 Gnic card (Christoph Goos) o Fix up PCI bus scans on some compaq machines o APM power management fixes/extra bios workarounds (Stephen Rothwell) o Merge 2.3.x fixes into 2.2.x Solo and CMPCI (Marcelo Tosatti) o Stop panic on SRM Alpha clearing the screen (Jay Estabrook) o EEPro100 turn off copy break mode and fix u32/ulong problem on Alpha (Jay Estabrook) o IDE geometry fix (Andries Brouwer) o Update IBM S/390 port (IBM) o Fix an IPv6 dependancy in the config.in (Arjan van de Ven) --- diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 86bc2c8ae808..fc2910877aa8 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -5966,10 +5966,11 @@ CONFIG_SK98LIN - SK-9844 (dual link 1000Base-SX) - SK-9821 (single link 1000Base-T) - SK-9822 (dual link 1000Base-T) + The adapters support Jumbo Frames. The dual link adapters support a link-failover feature. Read Documentation/networking/sk98lin.txt for information about optional driver parameters. - Questions concerning this driver may be addresse to: + Questions concerning this driver may be addressed to: linux@syskonnect.de If you want to compile this driver as a module ( = code which can be @@ -6609,7 +6610,7 @@ CONFIG_TLAN module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. - Please email feedback to james.banks@caldera.com. + Please email feedback to torben.mathiasen@compaq.com. VIA Rhine support CONFIG_VIA_RHINE @@ -9092,8 +9093,8 @@ CONFIG_APM machines with more than one CPU. Supporting software is available; for more information, read the - Battery Powered Linux mini-HOWTO, available via FTP (user: - anonymous) from ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/mini. + Battery Powered Linux mini-HOWTO, available from + http://www.linuxdoc.org/docs.html#howto . This driver does not spin down disk drives (see the hdparm(8) manpage ("man 8 hdparm") for that), and it doesn't turn off @@ -9106,9 +9107,7 @@ CONFIG_APM If you are running Linux on a laptop, you may also want to read the Linux Laptop home page on the WWW at - http://www.cs.utexas.edu/users/kharker/linux-laptop/ (to browse the - WWW, you need to have access to a machine on the Internet that has a - program like lynx or netscape). + http://www.cs.utexas.edu/users/kharker/linux-laptop/ . Generally, if you don't have a battery in your machine, there isn't much point in using this driver and you should say N. If you get @@ -9179,18 +9178,6 @@ CONFIG_APM_DISPLAY_BLANK backlight at all, or it might print a lot of errors to the console, especially if you are using gpm. -Power off on shutdown -CONFIG_APM_POWER_OFF - Enable the ability to power off the computer after the Linux kernel - is halted. You will need software (e.g., a suitable version of the - halt(8) command ("man 8 halt")) to cause the computer to power down. - Recent versions of the sysvinit package available from - ftp://metalab.unc.edu/pub/Linux/system/daemons/init/ (user: - anonymous) contain support for this ("halt -p" shuts down Linux and - powers off the computer, if executed from runlevel 0). As with the - other APM options, this option may not work reliably with some APM - BIOS implementations. - Ignore multiple suspend/standby events CONFIG_APM_IGNORE_MULTIPLE_SUSPEND This option is necessary on the IBM Thinkpad 560, but should work on @@ -9228,6 +9215,12 @@ CONFIG_APM_ALLOW_INTS many of the newer IBM Thinkpads. If you experience hangs when you suspend, try setting this to Y. Otherwise, say N. +Use real mode APM BIOS call to power off +CONFIG_APM_REAL_MODE_POWER_OFF + Use real mode APM BIOS calls to switch off the computer. This is + a work-around for a number of buggy BIOSes. Switch this option on if + your computer crashes instead of powering off properly. + Watchdog Timer Support CONFIG_WATCHDOG If you say Y here (and to one of the following options) and create a diff --git a/Documentation/Debugging390.txt b/Documentation/Debugging390.txt new file mode 100644 index 000000000000..7c67c06d0220 --- /dev/null +++ b/Documentation/Debugging390.txt @@ -0,0 +1,1941 @@ + + Debugging on Linux for 390 + by + Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + Best viewed with fixed width fonts + +Overview of Document: +===================== +This document is intended to give an good overview of how to debug +Linux for S390 it isn't intended as a complete reference & not a +tutorial on the fundamentals of C & assembly, it dosen't go into +390 IO in any detail. It is intended to compliment the following +books. +Enterprise Systems Architecture/390 Reference Summary SA22-7209-01 & the +Enterprise Systems Architecture/390 Principles of Operation SA22-7201-05 +& any other worthwhile references you get. + +It is intended like the Enterprise Systems Architecture/390 Reference Summary +to be printed out & used as a quick cheat sheet self help style reference when +problems occur. + +Contents +======== +S390 Register Set +Address Spaces on Linux +Address Spaces on Linux for S390 +The Linux for S390 Kernel Task Structure +Register Usage & Stackframes on Linux for S390 with glossary +Compiling programs for debugging on Linux for S390 +Figuring out gcc compile errors +Debugging Tools +objdump +strace +Debugging under VM +Stack Tracing under VM +S390 IO Overview +Debugging IO on S390 under VM +GDB on S390 +Stack chaining in gdb by hand +Examining core dumps +LDD +Debugging modules +The proc file system +Starting points for debugging scripting languages etc. + +S390 Register Set +================ +The current ESA 390 architecture has the following registers. + +16 32 bit General propose registers ( r0-r15 or gpr0-gpr15) used for arithmetic & addressing + +16 Control registers ( cr0-cr15 kernel usage only ) used for memory managment, +interrupt control,debugging control etc. + +16 Access registers ( ar0-ar15 ) not used by normal programs but potentially could +be used as temporary storage. Their main purpose is their 1 to 1 +association with general purpose registers and are used in +the kernel for copying data between address spaces. + +16 64 bit floating point registers (fp0-fp15 ) IEEE & HFP floating +point format compliant on G5 upwards & a Floating point control reg (FPC) +4 64 bit registers (fp0,fp2,fp4 & fp6) HFP only on older machines. +Note: +Linux (currently) always uses IEEE & emulates G5 IEEE format on older machines, +( provided the kernel is configured for this ). + + +The PSW is the most important register on the machine it +is 64 bit & serves the roles of a program counter (pc), +condition code register,memory space designator. +In IBM standard notation I am counting bit 0 as the MSB. +It has several advantages over a normal program counter +in that you can change address translation & program counter +in a single instruction. To change address translation, +e.g. switching address translation off requires that you +have a logical=physical mapping for the address you are +currently running at. + +Bit Value + +0 Reserved ( must be 0 ) otherwise specification exception occurs. + +1 Program Event Recording 1 PER enabled, + PER is used to facilititate debugging e.g. single stepping. + +2-4 Reserved ( must be 0 ). + +5 Dynamic address translation 1=DAT on. + +6 Input/Output interrupt Mask + +7 External interrupt Mask used primarily for interprocessor signalling & + clock interupts. + +8-12 PSW Key used for complex memory protection mechanism not used under linux + +13 Machine Check Mask 1=enable machine check interrupts + +14 Wait State set this to 1 to stop the processor except for interrupts & give + time to other LPARS used in CPU idle in the kernel to increase overall + usage of processor resources. + +15 Problem state ( if set to 1 certain instructions are disabled ) + all linux user programs run with this bit 1 ( useful info for debugging under VM ). + +16-17 Address Space Control + + 00 Primary Space Mode when DAT on + The linux kernel currently runs in this mode, CR1 is affiliated with this mode + & points to the primary segment table origin etc. + + 01 Access register mode this mode is used in functions to + copy data between kernel & user space. + + 10 Secondary space mode not used in linux however CR7 the + register affiliated with this mode is & this & normally + CR13=CR7 to allow us to copy data between kernel & user space. + We do this as follows: + We set ar2 to 0 to designate its + affiliated gpr ( gpr2 )to point to primary=kernel space. + We set ar4 to 1 to designate its + affiliated gpr ( gpr4 ) to point to secondary=home=user space + & then essentially do a memcopy(gpr2,gpr4,size) to + copy data between the address spaces, the reason we use home space for the + kernel & don't keep secondary space free is that code will not run in secondary space. + + 11 Home Space Mode all user programs run in this mode. + it is affiliated with CR13. + +18-19 Condition codes (CC) + +20 Fixed point overflow mask if 1=FPU exceptions for this event occur ( normally 0 ) + +21 Decimal overflow mask if 1=FPU exceptions for this event occur ( normally 0 ) + +22 Exponent underflow mask if 1=FPU exceptions for this event occur ( normally 0 ) + +23 Significance Mask if 1=FPU exceptions for this event occur ( normally 0 ) + +24-31 Reserved Must be 0. + +32 1=31 bit addressing mode 0=24 bit addressing mode (for backward compatibility ), + linux always runs with this bit set to 1 + +33-64 Instruction address. + +Prefix Page +----------- +This per cpu memory area is too intimately tied to the processor not to mention. +It exists between the real addresses 0-4096 on the processor & is exchanged +with a page in absolute storage by the set prefix instruction in linux'es startup. +This page different on each processor. +Bytes 0-512 ( 200 hex ) are used by the processor itself for holding such +information as exception indications & entry points for exceptions. +Bytes after 0xc00 hex are used by linux for per processor globals. +The closest thing to this on traditional architectures is the interrupt +vector table. This is a good thing & does simplify some of the kernel coding +however it means that we now cannot catch stray NULL pointers in the +kernel without hard coded checks. + + + +Address Spaces on Linux +======================= + +The traditional Intel Linux is approximately mapped as follows forgive +the ascii art. +0xFFFFFFFF 4GB Himem ***************** + * * + * Kernel Space * + * * + ***************** **************** +User Space Himem (typically 0xC0000000 3GB )* User Stack * * * + ***************** * * + * Shared Libs * * Next Process * + ***************** * to * + * * <== * Run * <== + * User Program * * * + * Data BSS * * * + * Text * * * + * Sections * * * +0x00000000 ***************** **************** + +Now it is easy to see that on Intel it is quite easy to recognise a kernel address +as being one greater than user space himem ( in this case 0xC0000000). +& addresses of less than this are the ones in the current running program on this +processor ( if an smp box ). +If using the virtual machine ( VM ) as a debugger it is quite difficult to +know which user process is running as the address space you are looking at +could be from any process in the run queue. +Thankfully you normally get lucky as address spaces don't overlap that & +you can recognise the code at by cross referencing with a dump made by objdump +( more about that later ). + +The limitation of Intels addressing technique is that the linux +kernel uses a very simple real address to virtual addressing technique +of Real Address=Virtual Address-User Space Himem. +This means that on Intel the kernel linux can typically only address +Himem=0xFFFFFFFF-0xC0000000=1GB & this is all the RAM these machines +can typically use. +They can lower User Himem to 2GB or lower & thus be +able to use 2GB of RAM however this shrinks the maximum size +of User Space from 3GB to 2GB they have a no win limit of 4GB unless +they go to 64 Bit. + + +On 390 our limitations & strengths make us slightly different. +For backward compatibility we are only allowed use 31 bits (2GB) +of our 32 bit addresses,however, we use entirely separate address +spaces for the user & kernel. + +This means we can support 2GB of non Extended RAM, & more +with the Extended memory managment swap device & 64 Bit +when it comes along. + + +Address Spaces on Linux for S390 +================================ + +Our addressing scheme is as follows + + +Himem 0x7fffffff 2GB ***************** **************** + * User Stack * * * + ***************** * * + * Shared Libs * * * + ***************** * * + * * * Kernel * + * User Program * * * + * Data BSS * * * + * Text * * * + * Sections * * * +0x00000000 ***************** **************** + +This also means that we need to look at the PSW problem state bit +or the addressing mode to decide whether we are looking at +user or kernel space. + +The Linux for S390 Kernel Task Structure +======================================== +Each process/thread under Linux for S390 has its own kernel task_struct +defined in linux/include/linux/sched.h +The S390 on initialisation & resuming of a process on a cpu sets +the __LC_KERNEL_STACK variable in the spare prefix area for this cpu +( which we use for per processor globals). + +The kernel stack pointer is intimately tied with the task stucture for +each processor as follows. + + ************************ + * 1 page kernel stack * + * ( 4K ) * + ************************ + * 1 page task_struct * + * ( 4K ) * +8K aligned ************************ + +What this means is that we don't need to dedicate any register or global variable +to point to the current running process & can retrieve it with the following +very simple construct + +static inline struct task_struct * get_current(void) +{ + struct task_struct *current; + __asm__("lhi %0,-8192\n\t" + "nr %0,15" + : "=r" (current) ); + return current; +} + +i.e. just anding the current kernel stack pointer with the mask -8192. +Thankfully because Linux dosen't have support for nested IO interrupts +& our devices have large buffers can survive interrupts being shut for +short amounts of time we don't need a separate stack for interrupts. + + + + +Register Usage & Stackframes on Linux for S390 +============================================== +Overview: +--------- +This is the code that gcc produces at the top & the bottom of +each function, it usually is fairly consistent & similar from +function to function & if you know its layout you can probalby +make some headway in finding the ultimate cause of a problem +after a crash without a source level debugger. + +Note: To follow stackframes requires a knowledge of C or Pascal & +limited knowledge of one assembly language. + +Glossary: +--------- +alloca: +This is a built in compiler function for runtime allocation +of extra space on the callers stack which is obviously freed +up on function exit ( e.g. the caller may choose to allocate nothing +of a buffer of 4k if required for temporary purposes ), it generates +very efficent code ( a few cycles ) when compared to alternatives +like malloc. + +automatics: These are local variables on the stack, +i.e they aren't in registers & they aren't static. + +back-chain: +This is a pointer to the stack pointer before entering a +framed functions ( see frameless function ) prologue got by +deferencing the address of the current stack pointer, + i.e. got by accessing the 32 bit value at the stack pointers +current location. + +base-pointer: +This is a pointer to the back of the literal pool which +is an area just behind each procedure used to store constants +in each function. + +call-clobbered: The caller probably needs to save these registers if there +is something of value in them, on the stack or elsewhere before making a +call to another procedure so that it can restore it later. + +epilogue: +The code generated by the compiler to return to the caller. + +frameless-function +A frameless function in Linux for 390 is one which doesn't need +more than the 96 bytes given to it by the caller. +A frameless function never: +1) Sets up a back chain. +2) Calls alloca. +3) Calls other normal functions +4) Has automatics. + +GOT-pointer: +This is a pointer to the global-offset-table in ELF +( Executable Linkable Format, Linux'es most common executable format ), +all globals & shared library objects are found using this pointer. + +lazy-binding +ELF shared libraries are typically only loaded when routines in the shared +library are actually first called at runtime. This is lazy binding. + +procedure-linkage-table +This is a table found from the GOT which contains pointers to routines +in other shared libraries which can't be called to by easier means. + +prologue: +The code generated by the compiler to set up the stack frame. + +outgoing-args: +This is extra area allocated on the stack of the calling function if the +parameters for the callee's cannot all be put in registers, the same +area can be reused by each function the caller calls. + +routine-descriptor: +A COFF executable format based concept of a procedure reference +actually being 8 bytes or more as opposed to a simple pointer to the routine. +This is typically defined as follows +Routine Descriptor offset 0=Pointer to Function +Routine Descriptor offset 4=Pointer to Table of Contents +The table of contents/TOC is roughly equivalent to a GOT pointer. +& it means that shared libraries etc. can be shared between several +environments each with their own TOC. + + +static-chain: This is used in nested functions a concept adopted from pascal +by gcc not used in ansi C or C++ ( although quite useful ), basically it +is a pointer used to reference local variables of enclosing functions. +You might come across this stuff once or twice in your lifetime. + +e.g. +The function below should return 11 though gcc may get upset & toss warnings +about unused variables. +int FunctionA(int a) +{ + int b; + FunctionC(int c) + { + b=c+1; + } + FunctionC(10); + return(b); +} + + +S390 Register usage +=================== +r0 used by syscalls/assembly call-clobbered +r1 used by syscalls/assembly call-clobbered +r2 argument 0 / return value 0 call-clobbered +r3 argument 1 / return value 1 (if long long) call-clobbered +r4 argument 2 call-clobbered +r5 argument 3 call-clobbered +r6 argument 5 saved +r7 pointer-to arguments 5 to ... saved +r8 this & that saved +r9 this & that saved +r10 static-chain ( if nested function ) saved +r11 frame-pointer ( if function used alloca ) saved +r12 got-pointer saved +r13 base-pointer saved +r14 return-address saved +r15 stack-pointer saved + +f0 argument 0 / return value ( float/double ) call-clobbered +f2 argument 1 call-clobbered +f4 saved +f6 saved +The remaining floating points +f1,f3,f5 f7-f15 are call-clobbered. + +Notes: +------ +1) The only requirement is that registers which are used +by the callee are saved, e.g. the compiler is perfectly +capible of using r11 for purposes other than a frame a +frame pointer if a frame pointer is not needed. +2) In functions with variable arguments e.g. printf the calling procedure +is identical to one without variable arguments & the same number of +parameters. However, the prologue of this function is somewhat more +hairy owing to it having to move these parameters to the stack to +get va_start, va_arg & va_end to work. +3) Access registers are currently unused by gcc but are used in +the kernel. Possibilities exist to use them at the moment for +temporary storage but it isn't recommended. +4) Only 4 of the floating point registers are used for +parameter passing as older machines such as G3 only have only 4 +& it keeps the stack frame compatible with other compilers. +However with IEEE floating point emulation under linux on the +older machines you are free to use the other 12. +5) A long long or double parameter cannot be have the +first 4 bytes in a register & the second four bytes in the +outgoing args area. It must be purely in the outgoing args +area if crossing this boundary. +6) Floating point parameters are mixed with outgoing args +on the outgoing args area in the order the are passed in as parameters. + +Stack Frame Layout +================== +0 back chain ( a 0 here signifies end of back chain ) +4 eos ( end of stack, not used on Linux for S390 used in other linkage formats ) +8 glue used in other linkage formats for saved routine descriptors etc. +12 glue used in other linkage formats for saved routine descriptors etc. +16 scratch area +20 scratch area +24 saved r6 of caller function +28 saved r7 of caller function +32 saved r8 of caller function +36 saved r9 of caller function +40 saved r10 of caller function +44 saved r11 of caller function +48 saved r12 of caller function +52 saved r13 of caller function +56 saved r14 of caller function +60 saved r15 of caller function +64 saved f4 of caller function +72 saved f6 of caller function +80 undefined +96 outgoing args passed from caller to callee +96+x possible stack alignment ( 8 bytes desirable ) +96+x+y alloca space of caller ( if used ) +96+x+y+z automatics of caller ( if used ) +0 back-chain + +A sample program with comments. +=============================== + +Comments on the function test +----------------------------- +1) It didn't need to set up a pointer to the constant pool gpr13 as it isn't used +( :-( ). +2) This is a frameless function & no stack is bought. +3) The compiler was clever enough to recognise that it could return the +value in r2 as well as use it for the passed in parameter ( :-) ). +4) The basr ( branch relative & save ) trick works as follows the instruction +has a special case with r0,r0 with some instruction operands is understood as +the literal value 0, some risc architectures also do this ). So now +we are branching to the next address & the address new program counter is +in r13,so now we subtract the size of the function prologue we have executed ++ the size of the literal pool to get to the top of the literal pool +0040037c int test(int b) +{ # Function prologue below + 40037c: 90 de f0 34 stm %r13,%r14,52(%r15) # Save registers r13 & r14 + 400380: 0d d0 basr %r13,%r0 # Set up pointer to constant pool using + 400382: a7 da ff fa ahi %r13,-6 # basr trick + return(5+b); + # Huge main program + 400386: a7 2a 00 05 ahi %r2,5 # add 5 to r2 + + # Function epilogue below + 40038a: 98 de f0 34 lm %r13,%r14,52(%r15) # restore registers r13 & 14 + 40038e: 07 fe br %r14 # return +} + +Comments on the function main +----------------------------- +1) The compiler did this function optimally ( 8-) ) + +Literal pool for main. +400390: ff ff ff ec .long 0xffffffec +main(int argc,char *argv[]) +{ # Function prologue below + 400394: 90 bf f0 2c stm %r11,%r15,44(%r15) # Save necessary registers + 400398: 18 0f lr %r0,%r15 # copy stack pointer to r0 + 40039a: a7 fa ff a0 ahi %r15,-96 # Make area for callee saving + 40039e: 0d d0 basr %r13,%r0 # Set up r13 to point to + 4003a0: a7 da ff f0 ahi %r13,-16 # literal pool + 4003a4: 50 00 f0 00 st %r0,0(%r15) # Save backchain + + return(test(5)); # Main Program Below + 4003a8: 58 e0 d0 00 l %r14,0(%r13) # load relative address of test from + # literal pool + 4003ac: a7 28 00 05 lhi %r2,5 # Set first parameter to 5 + 4003b0: 4d ee d0 00 bas %r14,0(%r14,%r13) # jump to test setting r14 as return + # address using branch & save instruction. + + # Function Epilogue below + 4003b4: 98 bf f0 8c lm %r11,%r15,140(%r15)# Restore necessary registers. + 4003b8: 07 fe br %r14 # return to do program exit +} + + +Compiling programs for debugging on Linux for S390 +================================================== +Make sure that the gcc is compiling & linking with the -g flag on. +This is typically done adding/appending the flags -g to the +CFLAGS & LDFLAGS variables Makefile of the program concerned. + +If using gdb & you would like accurate displays of registers & + stack traces compile without optimisation i.e make sure +that there is no -O2 or similar on the CFLAGS line of the Makefile & +the emitted gcc commands, obviously this will produce worse code +( not advisable for shipment ) but it is an aid to the debugging process. + +This aids debugging because the compiler will copy parameters passed in +in registers onto the stack so backtracing & looking at passed in +parameters will work, however some larger programs which use inline functions +will not compile without optimisation. + +Figuring out gcc compile errors +=============================== +If you are getting a lot of syntax errors compiling a program & the problem +isn't blatantly obvious from the source. +It often helps to just preprocess the file, this is done with the -E +option in gcc. +What this does is that it runs through the very first phase of compilation +( compilation in gcc is done in several stages & gcc calls many programs to +achieve its end result ) with the -E option gcc just calls the gcc preprocessor (cpp). +The c preprocessor does the following, it joins all the files #included together +recursively ( #include files can #include other files ) & also the c file you wish to compile. +It puts a fully qualified path of the #included files in a comment & it +does macro expansion. +This is useful for debugging because +1) You can double check whether the files you expect to be included are the ones +that are being included ( e.g. double check that you aren't going to the i386 asm directory ). +2) Check that macro definitions aren't clashing with typedefs, +3) Check that definitons aren't being used before they are being included. +4) Helps put the line emitting the error under the microscope if it contains macros. + +For convenience the Linux kernel's makefile will do preprocessing automatically for you +by suffixing the file you want built with .i ( instead of .o ) + +e.g. +from the linux directory type +make arch/s390/kernel/signal.i +this will build + +s390-gcc -D__KERNEL__ -I/home1/barrow/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +-fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -E arch/s390/kernel/signal.c +> arch/s390/kernel/signal.i + +Now look at signal.i you should see something like. + + +# 1 "/home1/barrow/linux/include/asm/types.h" 1 +typedef unsigned short umode_t; +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; + +If instead you are getting errors further down e.g. +unknown instruction:2515 "move.l" or better still unknown instruction:2515 +"Fixme not implemented yet, call Martin" you are probably are attempting to compile some code +meant for another architecture or code that is simply not implemented, with a fixme statement +stuck into the inline assembly code so that the author of the file now knows he has work to do. +To look at the assembly emitted by gcc just before it is about to call gas ( the gnu assembler ) +use the -S option. +Again for your convenience the Linux kernel's Makefile will hold your hand & +do all this donkey work for you also by building the file with the .s suffix. +e.g. +from the Linux directory type +make arch/s390/kernel/signal.s + +s390-gcc -D__KERNEL__ -I/home1/barrow/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +-fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -S arch/s390/kernel/signal.c +-o arch/s390/kernel/signal.s + + +This will output something like, ( please note the constant pool & the useful comments +in the prologue to give you a hand at interpreting it ). + +.LC54: + .string "misaligned (__u16 *) in __xchg\n" +.LC57: + .string "misaligned (__u32 *) in __xchg\n" +.L$PG1: # Pool sys_sigsuspend +.LC192: + .long -262401 +.LC193: + .long -1 +.LC194: + .long schedule-.L$PG1 +.LC195: + .long do_signal-.L$PG1 + .align 4 +.globl sys_sigsuspend + .type sys_sigsuspend,@function +sys_sigsuspend: +# leaf function 0 +# automatics 16 +# outgoing args 0 +# need frame pointer 0 +# call alloca 0 +# has varargs 0 +# incoming args (stack) 0 +# function length 168 + STM 8,15,32(15) + LR 0,15 + AHI 15,-112 + BASR 13,0 +.L$CO1: AHI 13,.L$PG1-.L$CO1 + ST 0,0(15) + LR 8,2 + N 5,.LC192-.L$PG1(13) + +Debugging Tools: +================ + +objdump +======= +This is a tool with many options the most useful being ( if compiled with -g). +objdump --source > + + +The whole kernel can be compiled like this ( Doing this will make a 17MB kernel +& a 200 MB listing ) however you have to strip it before building the image +using the strip command to make it a more reasonable size to boot it. + +A source/assembly mixed dump of the kernel can be done with the line +objdump --source vmlinux > vmlinux.lst +Also if the file isn't compiled -g this will output as much debugging information +as it can ( e.g. function names ), however, this is very slow as it spends lots +of time searching for debugging info, the following self explanitory line should be used +instead if the code isn't compiled -g. +objdump --disassemble-all --syms vmlinux > vmlinux.lst +as it is much faster + +As hard drive space is valuble most of us use the following approach. +1) Look at the emitted psw on the console to find the crash address in the kernel. +2) Look at the file System.map ( in the linux directory ) produced when building +the kernel to find the closest address less than the current PSW to find the +offending function. +3) use grep or similar to search the source tree looking for the source file + with this function if you don't know where it is. +4) rebuild this object file with -g on, as an example suppose the file was +( /arch/s390/kernel/signal.o ) +5) Assuming the file with the erroneous function is signal.c Move to the base of the +Linux source tree +6) rm /arch/s390/kernel/signal.o +7) make /arch/s390/kernel/signal.o +8) watch the gcc command line emitted +9) type it in again or alernatively cut & paste it on the console adding the -g option. +10) objdump --source arch/s390/kernel/signal.o > signal.lst +This will output the source & the assembly intermixed, as the snippet below shows +This will unfortunately output addresses which aren't the same +as the kernel ones you should be able to get around the mental arithmetic +by playing with the --adjust-vma parameter to objdump. + +extern inline void spin_lock(spinlock_t *lp) +{ + a0: 18 34 lr %r3,%r4 + a2: a7 3a 03 bc ahi %r3,956 + __asm__ __volatile(" lhi 1,-1\n" + a6: a7 18 ff ff lhi %r1,-1 + aa: 1f 00 slr %r0,%r0 + ac: ba 01 30 00 cs %r0,%r1,0(%r3) + b0: a7 44 ff fd jm aa + saveset = current->blocked; + b4: d2 07 f0 68 mvc 104(8,%r15),972(%r4) + b8: 43 cc + return (set->sig[0] & mask) != 0; +} + +6) If debugging under VM go down to that section in the document for more info. + + +strace: +------- +Q. What is it ? +A. It is a tool for intercepting calls to the kernel & logging them +to a file & on the screen. + +Q. What use is it ? +A. You can used it to find out what files a particular program opens. + + + +Example 1 +--------- +If you wanted to know does ping work but didn't have the source +strace ping -c 1 127.0.0.1 +& then look at the man pages for each of the syscalls below, +( In fact this is sometimes easier than looking at some spagetti +source which conditionally compiles for several architectures ) +Not everything that it throws out needs to make sense immeadiately + +Just looking quickly you can see that it is making up a RAW socket +for the ICMP protocol. +Doing an alarm(10) for a 10 second timeout +& doing a gettimeofday call before & after each read to see +how long the replies took, & writing some text to stdout so the user +has an idea what is going on. + +socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3 +getuid() = 0 +setuid(0) = 0 +stat("/usr/share/locale/C/libc.cat", 0xbffff134) = -1 ENOENT (No such file or directory) +stat("/usr/share/locale/libc/C", 0xbffff134) = -1 ENOENT (No such file or directory) +stat("/usr/local/share/locale/C/libc.cat", 0xbffff134) = -1 ENOENT (No such file or directory) +getpid() = 353 +setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 +setsockopt(3, SOL_SOCKET, SO_RCVBUF, [49152], 4) = 0 +fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(3, 1), ...}) = 0 +mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40008000 +ioctl(1, TCGETS, {B9600 opost isig icanon echo ...}) = 0 +write(1, "PING 127.0.0.1 (127.0.0.1): 56 d"..., 42PING 127.0.0.1 (127.0.0.1): 56 data bytes +) = 42 +sigaction(SIGINT, {0x8049ba0, [], SA_RESTART}, {SIG_DFL}) = 0 +sigaction(SIGALRM, {0x8049600, [], SA_RESTART}, {SIG_DFL}) = 0 +gettimeofday({948904719, 138951}, NULL) = 0 +sendto(3, "\10\0D\201a\1\0\0\17#\2178\307\36"..., 64, 0, {sin_family=AF_INET, +sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 64 +sigaction(SIGALRM, {0x8049600, [], SA_RESTART}, {0x8049600, [], SA_RESTART}) = 0 +sigaction(SIGALRM, {0x8049ba0, [], SA_RESTART}, {0x8049600, [], SA_RESTART}) = 0 +alarm(10) = 0 +recvfrom(3, "E\0\0T\0005\0\0@\1|r\177\0\0\1\177"..., 192, 0, +{sin_family=AF_INET, sin_port=htons(50882), sin_addr=inet_addr("127.0.0.1")}, [16]) = 84 +gettimeofday({948904719, 160224}, NULL) = 0 +recvfrom(3, "E\0\0T\0006\0\0\377\1\275p\177\0"..., 192, 0, +{sin_family=AF_INET, sin_port=htons(50882), sin_addr=inet_addr("127.0.0.1")}, [16]) = 84 +gettimeofday({948904719, 166952}, NULL) = 0 +write(1, "64 bytes from 127.0.0.1: icmp_se"..., +5764 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=28.0 ms + +Example 2 +--------- +strace passwd 2>&1 | grep open +produces the following output +open("/etc/ld.so.cache", O_RDONLY) = 3 +open("/opt/kde/lib/libc.so.5", O_RDONLY) = -1 ENOENT (No such file or directory) +open("/lib/libc.so.5", O_RDONLY) = 3 +open("/dev", O_RDONLY) = 3 +open("/var/run/utmp", O_RDONLY) = 3 +open("/etc/passwd", O_RDONLY) = 3 +open("/etc/shadow", O_RDONLY) = 3 +open("/etc/login.defs", O_RDONLY) = 4 +open("/dev/tty", O_RDONLY) = 4 + +The 2>&1 is done to redirect stderr to stdout & grep is then filtering this input +through the pipe for each line containing the string open. + + +Example 3 +--------- +Getting sophistocated +telnetd crashes on & I don't know why +Steps +----- +1) Replace the following line in /etc/inetd.conf +telnet stream tcp nowait root /usr/sbin/in.telnetd -h +with +telnet stream tcp nowait root /blah + +2) Create the file /blah with the following contents to start tracing telnetd +#!/bin/bash +/usr/bin/strace -o/t1 -f /usr/sbin/in.telnetd -h +3) chmod 700 /blah to make it executable only to root +4) +killall -HUP inetd +or ps aux | grep inetd +get inetd's process id +& kill -HUP inetd to restart it. + +Important options +----------------- +-o is used to tell strace to output to a file in our case t1 in the root directory +-f is to follow children i.e. +e.g in our case above telnetd will start the login process & subsequently a shell like bash. +You will be able to tell which is which from the process ID's listed on the left hand side +of the strace output. +-p will tell strace to attach to a running process, yup this can be done provided + it isn't being traced or debugged already & you have enough privileges, +the reason 2 processes cannot trace or debug the same program is that strace +becomes the parent process of the one being debugged & processes ( unlike people ) +can have only one parent. + + +However the file /t1 will get big quite quickly +to test it telnet 127.0.0.1 + +now look at what files in.telnetd execve'd +413 execve("/usr/sbin/in.telnetd", ["/usr/sbin/in.telnetd", "-h"], [/* 17 vars */]) = 0 +414 execve("/bin/login", ["/bin/login", "-h", "localhost", "-p"], [/* 2 vars */]) = 0 + +Whey it worked!. + + +Other hints: +------------ +If the program is not very interactive ( i.e. not much keyboard input ) +& is crashing in one architecture but not in another you can do +an strace of both programs under as identical a scenario as you can +on both architectures outputting to a file then. +do a diff of the two traces using the diff program +i.e. +diff output1 output2 +& maybe you'll be able to see where the call paths differed, this +is possibly near the cause of the crash. + +More info +--------- +Look at man pages for strace & the various syscalls +e.g. man strace, man alarm, man socket. + + +Debugging under VM +================== + +Notes +----- +Addresses & values in the VM debugger are always hex never decimal +Address ranges are of the format - or . +e.g. The address range 0x2000 to 0x3000 can be described described as +2000-3000 or 2000.1000 + +The VM Debugger is case insensitive. + +VM's strengths are usually other debuggers weaknesses you can get at any resource +no matter how sensitive e.g. memory managment resources,change address translation +in the PSW. For kernel hacking you will reap dividends if you get good at it. + +The VM Debugger displays operators but not operands, probably because some +of it was written when memory was expensive & the programmer was probably proud that +it fitted into 2k of memory & the programmers & didn't want to shock hardcore VM'ers by +changing the interface :-), also the debugger displays useful information on the same line & +the author of the code probably felt that it was a good idea not to go over +the 80 columns on the screen. + +As some of you are probably in a panic now this isn't as unintuitive as it may seem +as the 390 instructions are easy to decode mentally & you can make a good guess at a lot +of them as all the operands are nibble ( half byte aligned ) & if you have an objdump listing +also it is quite easy to follow, if you don't have an objdump listing keep a copy of +the ESA Reference Summary & look at between pages 2 & 7 or alternatively the +ESA principles of operation. +e.g. even I can guess that +0001AFF8' LR 180F CC 0 +is a ( load register ) lr r0,r15 + +Also it is very easy to tell the length of a 390 instruction from the 2 most significant +bits in the instruction ( not that this info is really useful except if you are trying to +make sense of a hexdump of code ). +Here is a table +Bits Instruction Length +------------------------------------------ +00 2 Bytes +01 4 Bytes +10 4 Bytes +11 6 Bytes + + + + +The debugger also displays other useful info on the same line such as the +addresses being operated on destination addresses of branches & condition codes. +e.g. +00019736' AHI A7DAFF0E CC 1 +000198BA' BRC A7840004 -> 000198C2' CC 0 +000198CE' STM 900EF068 >> 0FA95E78 CC 2 + + + +Useful VM debugger commands +=========================== + +I suppose I'd better mention this before I start +to list the current active traces do +Q TR +there can be a maximum of 255 of these per set +( more about trace sets later ). +To stop traces issue a +TR END. +To delete a particular breakpoint issue +TR DEL + +The PA1 key drops to CP mode so you can issue debugger commands, +Doing alt c (on my 3270 console at least ) clears the screen. +hitting b comes back to the running operating system +from cp mode ( in our case linux ). +It is typically useful to add shortcuts to your profile.exec file +if you have one ( this is roughly equivalent to autoexec.bat in DOS ). +file here are a few from mine. +/* this gives me command history on issuing f12 */ +set pf12 retrieve +/* this continues */ +set pf8 imm b +/* goes to trace set a */ +set pf1 imm tr goto a +/* goes to trace set b */ +set pf2 imm tr goto b +/* goes to trace set c */ +set pf3 imm tr goto c + + + +Instruction Tracing +------------------- +Setting a simple breakpoint +TR I PSWA
+To debug a particular function try +TR I R +TR I on its own will single step. +TR I DATA will trace for particular mnemonics +e.g. +TR I DATA 4D R 0197BC.4000 +will trace for BAS'es ( opcode 4D ) in the range 0197BC.4000 +if you were inclined you could add traces for all branch instructions & +suffix them with the run prefix so you would have a backtrace on screen +when a program crashes. +TR BR will trace branches into or out of an address. +e.g. +TR BR INTO 0 is often quite useful if a program is getting awkward & deciding +to branch to 0 & crashing as this will stop at the address before in jumps to 0. + + + +Displaying & modifying Registers +-------------------------------- +D G will display all the gprs +D X will display all the control registers +D AR will display all the access registers +D AR4-7 will display access registers 4 to 7 +CPU ALL D G will display the GRPS of all CPUS in the configuration +D PSW will display the current PSW +st PSW 2000 will put the value 2000 into the PSW & +cause crash your machine. +D PREFIX + + +Displaying Memory +----------------- +To display memory mapped using the current PSW's mapping try +D +To make VM display a message each time it hits a particular address & continue try +D I will disassemble/display a range of instructions. +ST addr 32 bit word will store a 32 bit aligned address +D T will display the EBCDIC in an address ( if you are that way inclined ) +D R will display real addresses ( without DAT ) but with prefixing. +There are other complex options to display if you need to get at say home space +but are in primary space the easiest thing to do is to temporarily +modify the PSW to the other addressing mode, display the stuff & then +restore it. + +Hints +----- +If you want to issue a debugger command without halting your virtual machine with the +PA1 key try prefixing the command with #CP e.g. +#cp tr i pswa 2000 +also suffixing most debugger commands with RUN will cause them not +to stop just display the mnemonic at the current instruction on the console. +If you have several breakpoints you want to put into your program & +you get fed up of cross referencing with System.map +you can do the following trick for several symbols. +grep do_signal System.map +which emits the following among other things +0001f4e0 T do_signal +now you can do +TR I PSWA 0001f4e0 cmd msg * do_signal +This sends a message to your own console each time do_signal is entered. +( As an aside I wrote a perl script once which automatically generated a REXX +script with breakpoints on every kernel procedure, this isn't a good idea +because there are thousands of these routines & VM can only set 255 breakpoints +at a time so you nearly had to spend as long pruning the file down as you would +entering the msg's by hand ),however, the trick might be useful for a single object file. + + +Tracing Program Exceptions +-------------------------- +If you get a crash which says something like +illegal operation or specification exception followed by a register dump +You can restart linux & trace these using the tr prog trace option. + +The most common ones you will normally be tracing for is +1=operation exception +2=privileged operation exception +4=protection exception +5=addressing exception +6=specification exception +10=segment translation exception +11=page translation exception + +The full list of these is on page 22 of the current ESA Reference Summary. +e.g. +tr prog 10 will trace segment translation exceptions. +tr prog on its own will trace all program interruption codes. + +Trace Sets +---------- +On starting VM you are initially in the INITIAL trace set. +You can do a Q TR to verify this. +If you have a complex tracing situation where you wish to wait for instance +till a driver is open before you start tracing IO, but know in your +heart that you are going to have to make several runs through the code till you +have a clue whats going on. + +What you can do is +TR I PSWA +hit b to continue till breakpoint +reach the breakpoint +now do your +TR GOTO B +TR IO 7c08-7c09 or whatever & trace tour IO +to got back to the initial trace set do +TR GOTO INITIAL +& the TR I PSWA will be the only active breakpoint again. + + +Tracing linux syscalls under VM +------------------------------- +Syscalls are implemented on Linux for S390 by the Supervisor call instruction (SVC) there 256 +possibilities of these as the instruction is made up of a 0xA opcode & the second byte being +the syscall number. They are traced using the simple command. +TR SVC +the syscalls are defined in linux/include/asm-s390/unistd.h +e.g. to trace all file opens just do +TR SVC 5 ( as this is the syscall number of open ) + + +SMP Specific commands +--------------------- +To find out how many cpus you have +Q CPUS displays all the CPU's available to your virtual machine +To find the cpu that the current cpu VM debugger commands are being directed at do +Q CPU to change the current cpu cpu VM debugger commands are being directed at do +CPU + +On a SMP guest issue a command to all CPUs try prefixing the command with cpu all. +To issue a command to a particular cpu try cpu e.g. +CPU 01 TR I R 2000.3000 +If you are running on a guest with several cpus & you have a IO related problem +& cannot follow the flow of code but you know it isnt smp related. +from the bash prompt issue +shutdown -h now or halt. +do a Q CPUS to find out how many cpus you have +detach each one of them from cp except cpu 0 +by issueing a +DETACH CPU 01-(number of cpus in configuration) +& reboot linux. +TR SIGP will trace inter processor signal processor instructions. + + +Help for displaying ascii textstrings +------------------------------------- +As textstrings are cannot be displayed in ASCII under the VM debugger ( I love EBDIC too ) I have +written this little program which will convert a command line of hex digits to ascii text +which can be compiled under linux & you can copy the hex digits from your x3270 terminal to +your xterm if you are debugging from a linuxbox. + +This is quite useful when looking at a parameter passed in as a text string +under VM ( unless you are good at decoding ASCII in your head ). + +e.g. consider tracing an open syscall +TR SVC 5 +We have stopped at a breakpoint +000151B0' SVC 0A05 -> 0001909A' CC 0 + +D 20.8 to check the SVC old psw in the prefix area & see was it from userspace +( for the layout of the prefix area consult P18 of the ESA 390 Reference Summary +if you have it available ). +V00000020 070C2000 800151B2 +The problem state bit wasn't set & it's also too early in the boot sequence +for it to be a userspace SVC if it was we would have to temporarily switch the +psw to user space addressing so we could get at the first parameter of the open in +gpr2. +Next do a +D G2 +GPR 2 = 00014CB4 +Now display what gpr2 is pointing to +D 00014CB4.20 +V00014CB4 2F646576 2F636F6E 736F6C65 00001BF5 +V00014CC4 FC00014C B4001001 E0001000 B8070707 +Now copy the text till the first 00 hex ( which is the end of the string +to an xterm & do hex2ascii on it. +hex2ascii 2F646576 2F636F6E 736F6C65 00 +outputs +Decoded Hex:=/ d e v / c o n s o l e 0x00 +We were opening the console device, + +You can compile the code below yourself for practice :-), +/* + * hex2ascii.c + * a useful little tool for converting a hexadecimal command line to ascii + * + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation. + */ +#include + +int main(int argc,char *argv[]) +{ + int cnt1,cnt2,len,toggle=0; + int startcnt=1; + unsigned char c,hex; + + if(argc>1&&(strcmp(argv[1],"-a")==0)) + startcnt=2; + printf("Decoded Hex:="); + for(cnt1=startcnt;cnt1='0'&&c<='9') + c=c-'0'; + if(c>='A'&&c<='F') + c=c-'A'+10; + if(c>='a'&&c<='F') + c=c-'a'+10; + switch(toggle) + { + case 0: + hex=c<<4; + toggle=1; + break; + case 1: + hex+=c; + if(hex<32||hex>127) + { + if(startcnt==1) + printf("0x%02X ",(int)hex); + else + printf("."); + } + else + { + printf("%c",hex); + if(startcnt==1) + printf(" "); + } + toggle=0; + break; + } + } + } + printf("\n"); +} + +Stack tracing under VM +---------------------- +A basic backtrace +----------------- + +Here are the tricks I use 9 out of 10 times it works pretty well, + +When your backchain reaches a dead end +-------------------------------------- +This can happen when an exception happens in the kernel & the kernel is entered twice +if you reach the NULL pointer at the end of the back chain you should be +able to sniff further back if you follow the following tricks. +1) A kernel address should be easy to recognise since it is in +primary space & the problem state bit isn't set & also +The Hi bit of the address is set. +2) Another backchain should also be easy to recognise since it is an +address pointing to another address approximately 100 bytes or 0x70 hex +behind the current stackpointer. + + +Here is some practice. +boot the kernel & hit PA1 at some random time +d g to display the gprs, this should display something like +GPR 0 = 00000001 00156018 0014359C 00000000 +GPR 4 = 00000001 001B8888 000003E0 00000000 +GPR 8 = 00100080 00100084 00000000 000FE000 +GPR 12 = 00010400 8001B2DC 8001B36A 000FFED8 +Note that GPR14 is a return address but as we are real men we are going to +trace the stack. +display 0x40 bytes after the stack pointer. + +V000FFED8 000FFF38 8001B838 80014C8E 000FFF38 +V000FFEE8 00000000 00000000 000003E0 00000000 +V000FFEF8 00100080 00100084 00000000 000FE000 +V000FFF08 00010400 8001B2DC 8001B36A 000FFED8 + + +Ah now look at whats in sp+56 (sp+0x38) this is 8001B36A our saved r14 if +you look above at our stackframe & also agrees with GPR14. + +now backchain +d 000FFF38.40 +we now are taking the contents of SP to get our first backchain. + +V000FFF38 000FFFA0 00000000 00014995 00147094 +V000FFF48 00147090 001470A0 000003E0 00000000 +V000FFF58 00100080 00100084 00000000 001BF1D0 +V000FFF68 00010400 800149BA 80014CA6 000FFF38 + +This displays a 2nd return address of 80014CA6 + +now do d 000FFFA0.40 for our 3rd backchain + +V000FFFA0 04B52002 0001107F 00000000 00000000 +V000FFFB0 00000000 00000000 FF000000 0001107F +V000FFFC0 00000000 00000000 00000000 00000000 +V000FFFD0 00010400 80010802 8001085A 000FFFA0 + + +our 3rd return address is 8001085A + +as the 04B52002 looks suspiciously like rubbish it is fair to assume that the kernel entry routines +for the sake of optimisation dont set up a backchain. + +now look at System.map to see if the addresses make any sense. + +grep -i 0001b3 System.map +outputs among other things +0001b304 T cpu_idle +so 8001B36A +is cpu_idle+0x66 ( quiet the cpu is asleep, don't wake it ) + + +grep -i 00014 System.map +produces among other things +00014a78 T start_kernel +so 0014CA6 is start_kernel+some hex number I can't add in my head. + +grep -i 00108 System.map +this produces +00010800 T _stext +so 8001085A is _stext+0x5a + +Congrats you've done your first backchain. + + + +S390 IO Overview +================ + +I am not going to give a course in 390 IO architecture as this would take me quite a +while & I'm no expert. Instead I'll give a 390 IO architecture summary for Dummies if you have +the ESA principles of operation available read this instead. If nothing else you may find a few +useful keywords in here & be able to use them on a web search engine like altavista to find +more useful information. + +Unlike other bus architectures modern 390 systems do their IO using mostly +fibre optics & devices such as tapes & disks can be shared between several mainframes, +also S390 can support upto 65536 devices while a high end PC based system might be choking +with around 64. Here is some of the common IO terminology + +Subchannel: +This is the logical number most IO commands use to talk to an IO device there can be upto +0x10000 (65536) of these in a configuration typically there is a few hundred. Under VM +for simplicity they are allocated contiguously, however on the native hardware they are not +they typically stay consistent between boots provided no new hardware is inserted or removed. +Under Linux for 390 we use these as IRQ's & also when issuing an IO command (CLEAR SUBCHANNEL, +HALT SUBCHANNEL,MODIFY SUBCHANNEL,RESUME SUBCHANNEL,START SUBCHANNEL,STORE SUBCHANNEL & +TEST SUBCHANNEL ) we use this as the ID of the device we wish to talk to, the most +important of these instructions are START SUBCHANNEL ( to start IO ), TEST SUBCHANNEL ( to check +whether the IO completed successfully ), & HALT SUBCHANNEL ( to kill IO ), a subchannel +can have up to 8 channel paths to a device this offers redunancy if one is not available. + + +Device Number: +This number remains static & Is closely tied to the hardware, there are 65536 of these +also they are made up of a CHPID ( Channel Path ID, the most significant 8 bits ) +& another lsb 8 bits. These remain static even if more devices are inserted or removed +from the hardware, there is a 1 to 1 mapping between Subchannels & Device Numbers provided +devices arent inserted or removed. + +Channel Control Words: +CCWS are linked lists of instructions initially pointed to by an operation request block (ORB), +which is initially given to Start Subchannel (SSCH) command along with the subchannel number +for the IO subsystem to process while the CPU continues executing normal code. +These come in two flavours, Format 0 ( 24 bit for backward ) +compatibility & Format 1 ( 31 bit ). These are typically used to issue read & write +( & many other instructions ) they consist of a length field & an absolute address field. +For each IO typically get 1 or 2 interrupts one for channel end ( primary status ) when the +channel is idle & the second for device end ( secondary status ) sometimes you get both +concurrently, you check how the IO went on by issueing a TEST SUBCHANNEL at each interrupt, +from which you receive an Interruption response block (IRB). If you get channel & device end +status in the IRB without channel checks etc. your IO probably went okay. If you didn't you +probably need a doctorto examine the IRB & extended status word etc. +If an error occurs more sophistocated control units have a facitity known as +concurrent sense this means that if an error occurs Extended sense information will +be presented in the Extended status word in the IRB if not you have to issue a +subsequent SENSE CCW command after the test subchannel. + + +TPI( Test pending interrupt) can also be used for polled IO but in multitasking multiprocessor +systems it isn't recommended except for checking special cases ( i.e. non looping checks for +pending IO etc. ). + +Store Subchannel & Modify Subchannel can be used to examine & modify operating characteristics +of a subchannel ( e.g. channel paths ). + +Other IO related Terms: +Sysplex: S390's Clustering Technology +QDIO: S390's new high speed IO architecture to support devices such as gigabit ethernet, +this architecture is also designed to be forward compatible with up & coming 64 bit machines. + + +General Concepts + +Input Output Processors (IOP's) are responsible for communicating between +the mainframe CPU's & the channel & relieve the mainframe CPU's from the +burden of communicating with IO devices directly, this allows the CPU's to +concentrate on data processing. + +IOP's can use one or more links ( known as channel paths ) to talk to each +IO device. It first checks for path availability & chooses an available one, +then starts ( & sometimes terminates IO ). +There are two types of channel path ESCON & the Paralell IO interface. + +IO devices are attached to control units, control units provide the +logic to interface the channel paths & channel path IO protocols to +the IO devices, they can be integrated with the devices or housed separately +& often talk to several similar devices ( typical examples would be raid +controllers or a control unit which connects to 1000 3270 terminals ). + + + +---------------------------------------------------------------+ + | +-----+ +-----+ +-----+ +-----+ +----------+ +----------+ | + | | CPU | | CPU | | CPU | | CPU | | Main | | Expanded | | + | | | | | | | | | | Memory | | Storage | | + | +-----+ +-----+ +-----+ +-----+ +----------+ +----------+ | + |---------------------------------------------------------------+ + | IOP | IOP | IOP | + |--------------------------------------------------------------- + | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | + ---------------------------------------------------------------- + || || + || Bus & Tag Channel Path || ESCON + || ====================== || Channel + || || || || Path + +----------+ +----------+ +----------+ + | | | | | | + | CU | | CU | | CU | + | | | | | | + +----------+ +----------+ +----------+ + | | | | | ++----------+ +----------+ +----------+ +----------+ +----------+ +|I/O Device| |I/O Device| |I/O Device| |I/O Device| |I/O Device| ++----------+ +----------+ +----------+ +----------+ +----------+ + CPU = Central Processing Unit + C = Channel + IOP = IP Processor + CU = Control Unit + +The 390 IO systems come in 2 flavours the current 390 machines support both + +The Older 360 & 370 Interface,sometimes called the paralell I/O interface, +sometimes called Bus-and Tag & sometimes Original Equipment Manufacturers +Interface (OEMI). + +This byte wide paralell channel path/bus has parity & data on the "Bus" cable +& control lines on the "Tag" cable. These can operate in byte multiplex mode for +sharing between several slow devices or burst mode & monopolize the channel for the +whole burst. Upto 256 devices can be addressed on one of these cables. These cables are +about one inch in diameter. The maximum unextended length supported by these cables is +125 Meters but this can be extended up to 2km with a fibre optic channel extended +such as a 3044. The maximum burst speed supported is 4.5 megabytes per second however +some really old processors support only transfer rates of 3.0, 2.0 & 1.0 MB/sec. +One of these paths can be daisy chained to up to 8 control units. + + +ESCON if fibre optic it is also called FICON +Was introduced by IBM in 1990. Has 2 fibre optic cables & uses either leds or lasers +for communication at a signaling rate of upto 200 megabits/sec. As 10bits are transferred +for every 8 bits info this drops to 160 megabits/sec & to 18.6 Megabytes/sec once +control info & CRC are added. ESCON only operates in burst mode. + +ESCONs typical max cable length is 3km for the led version & 20km for the laser version +known as XDF ( extended distance facility ). This can be further extended by using an +ESCON director which triples the above mentioned ranges. Unlike Bus & Tag as ESCON is +serial it uses a packet switching architecture the standard Bus & Tag control protocol +is however present within the packets. Upto 256 devices can be attached to each control +unit that uses one of these interfaces. + +Common 390 Devices include: +Network adapters typically OSA2,3172's,2116's & OSA-E gigabit ethernet adapters, +Consoles 3270 & 3215 ( a teletype emulated under linux for a line mode console ). +DASD's direct access storage devices ( otherwise known as hard disks ). +Tape Drives. +CTC ( Channel to Channel Adapters ), +ESCON or Paralell Cables used as a very high speed serial link +between 2 machines. We use 2 cables under linux to do a bi-directional serial link. + + +Debugging IO on S390 under VM +============================= + +Now we are ready to go on with IO tracing commands under VM + +A few self explanatory queries: +Q OSA +Q CTC +Q DISK +Q DASD + +Q osa on my machine returns +OSA 7C08 ON OSA 7C08 SUBCHANNEL = 0000 +OSA 7C09 ON OSA 7C09 SUBCHANNEL = 0001 +OSA 7C14 ON OSA 7C14 SUBCHANNEL = 0002 +OSA 7C15 ON OSA 7C15 SUBCHANNEL = 0003 + +Now using the device numbers returned by this command we will +Trace the io starting up on the first device 7c08 & 7c09 +In our simplest case we can trace the +start subchannels +like TR SSCH 7C08-7C09 +or the halt subchannels +or TR HSCH 7C08-7C09 +MSCH's ,STSCH's I think you can guess the rest + +Ingo's favourite trick is tracing all the IO's & CCWS & spooling them into the reader of another +VM guest so he can ftp the logfile back to his own machine.I'll do a small bit of this & give you + a look at the output. + +1) Spool stdout to VM guest linux4's reader +SP PRT TO * +2) Fill linux4's reader with the trace +TR IO 7c08-7c09 INST INT CCW PRT RUN +3) Start up linux +i 00c +4) Finish the trace +TR END +5) close the reader +C PRT +6) list reader contents +RDRLIST +7) copy it to linux4's minidisk +RECEIVE / LOG TXT A1 ( replace +8) +filel & press F11 to look at it +You should see someting like. + +00020942' SSCH B2334000 0048813C CC 0 SCH 0000 DEV 7C08 + CPA 000FFDF0 PARM 00E2C9C4 KEY 0 FPI C0 LPM 80 + CCW 000FFDF0 E4200100 00487FE8 0000 E4240100 ........ + IDAL 43D8AFE8 + IDAL 0FB76000 +00020B0A' I/O DEV 7C08 -> 000197BC' SCH 0000 PARM 00E2C9C4 +00021628' TSCH B2354000 >> 00488164 CC 0 SCH 0000 DEV 7C08 + CCWA 000FFDF8 DEV STS 0C SCH STS 00 CNT 00EC + KEY 0 FPI C0 CC 0 CTLS 4007 +00022238' STSCH B2344000 >> 00488108 CC 0 SCH 0000 DEV 7C08 + +If you don't like messing up your readed ( because you possibly booted from it ) +you can alternatively spool it to another readers guest. + + +GDB on S390 +=========== +N.B. if compiling for debugging gdb works better without optimisation +( see Compiling programs for debugging ) + +invocation +---------- +gdb + +Online help +----------- +help: gives help on commands +e.g. +help +help display +Note gdb's online help is very good use it. + + +Assembly +-------- +info registers: displays registers other than floating point. +info all-registers: displays floating points as well. +disassemble: dissassembles +e.g. +disassemble without parameters will disassemble the current function +disassemble $pc $pc+10 + +Viewing & modifying variables +----------------------------- +print or p: displays variable or register +e.g. p/x $sp will display the stack pointer + +display: prints variable or register each time program stops +e.g. +display/x $pc will display the program counter +display argc + +undisplay : undo's display's + +info breakpoints: shows all current breakpoints + +info stack: shows stack back trace ( if this dosent work too well, I'll show you the +stacktrace by hand below ). + +info locals: displays local variables. + +info args: display current procedure arguments. + +set args: will set argc & argv each time the victim program is invoked. + +set =value +set argc=100 +set $pc=0 + + + +Modifying execution +------------------- +step: steps n lines of sourcecode +step steps 1 line. +step 100 steps 100 lines of code. + +next: like step except this will not step into subroutines + +stepi: steps a single machine code instruction. +e.g. stepi 100 + +nexti: steps a single machine code instruction but will not step into subroutines. + +finish: will run until exit of the current routine + +run: (re)starts a program + +cont: continues a program + +quit: exits gdb. + + +breakpoints +------------ + +break +sets a breakpoint +e.g. + +break main + +break *$pc + +break *0x400618 + +heres a really useful one for large programs +rbr +Set a breakpoint for all functions matching REGEXP +e.g. +rbr 390 +will set a breakpoint with all functions with 390 in their name. + +info breakpoints +lists all breakpoints + +delete: delete breakpoint by number or delete them all +e.g. +delete 1 will delete the first breakpoint +delete will delete them all + +watch: This will set a watchpoint ( usually hardware assisted ), +This will watch a variable till it changes +e.g. +watch cnt, will watch the variable cnt till it changes. +As an aside unfortunately gdb's, architecture independent watchpoint code +is inconsistent & not very good, watchpoints usually work but not always. + +info watchpoints: Display currently active watchpoints + +condition: ( another useful one ) +Specify breakpoint number N to break only if COND is true. +Usage is `condition N COND', where N is an integer and COND is an +expression to be evaluated whenever breakpoint N is reached. + + + +User defined functions/macros +----------------------------- +define: ( Note this is very very useful,simple & powerful ) +usage define end + +examples which you should consider putting into .gdbinit in your home directory +define d +stepi +disassemble $pc $pc+10 +end + +define e +nexti +disassemble $pc $pc+10 +end + + +Other hard to classify stuff +---------------------------- +signal n: +sends the victim program a signal. +e.g. signal 3 will send a SIGQUIT. + +info signals: +what gdb does when the victim receives certain signals. + +list: +e.g. +list lists current function source +list 1,10 list first 10 lines of curret file. +list test.c:1,10 + + +directory: +Adds directories to be searched for source if gdb cannot find the source. +(note it is a bit sensititive about slashes ) +e.g. To add the root of the filesystem to the searchpath do +directory // + + +call +This calls a function in the victim program, this is pretty powerful +e.g. +(gdb) call printf("hello world") +outputs: +$1 = 11 + +You might now be thinking that the line above didn't work, something extra had to be done. +(gdb) call fflush(stdout) +hello world$2 = 0 +As an aside the debugger also calls malloc & free under the hood +to make space for the "hello world" string. + + + +hints +----- +1) command completion works just like bash +( if you are a bad typist like me this really helps ) +e.g. hit br & cursor up & down :-). + +2) if you have a debugging problem that takes a few steps to recreate +put the steps into a file called .gdbinit in your current working directory +if you have defined a few extra useful user defined commands put these in +your home directory & they will be read each time gdb is launched. + +A typical .gdbinit file might be. +break main +run +break runtime_exception +cont + + +stack chaining in gdb by hand +----------------------------- +This is done using a the same trick described for VM +p/x (*($sp+56))&0x7fffffff get the first backchain. +this outputs +$5 = 0x528f18 +on my machine. +Now you can use +info symbol (*($sp+56))&0x7fffffff +you might see something like. +rl_getc + 36 in section .text telling you what is located at address 0x528f18 +Now do. +p/x (*(*$sp+56))&0x7fffffff +This outputs +$6 = 0x528ed0 +Now do. +info symbol (*(*$sp+56))&0x7fffffff +rl_read_key + 180 in section .text +now do +p/x (*(**$sp+56))&0x7fffffff +& so on. + + + +Note: Remember gdb has history just like bash you don't need to retype the +whole line just use the up & down arrows. + + + +For more info +------------- +From your linuxbox do +man gdb or info gdb. + +core dumps +---------- +What a core dump ?, +A core dump is a file generated by the kernel ( if allowed ) which contains the registers, +& all active pages of the program which has crashed. +From this file gdb will allow you to look at the registers & stack trace & memory of the +program as if it just crashed on your system, it is usually called core & created in the +current working directory. +This is very useful in that a customer can mail a core dump to a technical support department +& the technical support department can reconstruct what happened. +Provided the have an indentical copy of this program with debugging symbols compiled in & +the source base of this build is available. +In short it is far more useful than something like a crash log could ever hope to be. + +In theory all that is missing to restart a core dumped program is a kernel patch which +will do the following. +1) Make a new kernel task structure +2) Reload all the dumped pages back into the kernels memory managment structures. +3) Do the required clock fixups +4) Get all files & network connections for the process back into an identical state ( really difficult ). +5) A few more difficult things I haven't thought of. + + + +Why have I never seen one ?. +Probably because you haven't used the command +ulimit -c unlimited in bash +to allow core dumps, now do +ulimit -a +to verify that the limit was accepted. + +A sample core dump +To create this I'm going to do +ulimit -c unlimited +gdb +to launch gdb (my victim app. ) now be bad & do the following from another +telnet/xterm session to the same machine +ps -aux | grep gdb +kill -SIGSEGV +or alternatively use killall -SIGSEGV gdb if you have the killall command. +Now look at the core dump. +./gdb ./gdb core +Displays the following +GNU gdb 4.18 +Copyright 1998 Free Software Foundation, Inc. +GDB is free software, covered by the GNU General Public License, and you are +welcome to change it and/or distribute copies of it under certain conditions. +Type "show copying" to see the conditions. +There is absolutely no warranty for GDB. Type "show warranty" for details. +This GDB was configured as "s390-ibm-linux"... +Core was generated by `./gdb'. +Program terminated with signal 11, Segmentation fault. +Reading symbols from /usr/lib/libncurses.so.4...done. +Reading symbols from /lib/libm.so.6...done. +Reading symbols from /lib/libc.so.6...done. +Reading symbols from /lib/ld-linux.so.2...done. +#0 0x40126d1a in read () from /lib/libc.so.6 +Setting up the environment for debugging gdb. +Breakpoint 1 at 0x4dc6f8: file utils.c, line 471. +Breakpoint 2 at 0x4d87a4: file top.c, line 2609. +(top-gdb) info stack +#0 0x40126d1a in read () from /lib/libc.so.6 +#1 0x528f26 in rl_getc (stream=0x7ffffde8) at input.c:402 +#2 0x528ed0 in rl_read_key () at input.c:381 +#3 0x5167e6 in readline_internal_char () at readline.c:454 +#4 0x5168ee in readline_internal_charloop () at readline.c:507 +#5 0x51692c in readline_internal () at readline.c:521 +#6 0x5164fe in readline (prompt=0x7ffff810 "\177ÿøx\177ÿ÷Ø\177ÿøxÀ") + at readline.c:349 +#7 0x4d7a8a in command_line_input (prrompt=0x564420 "(gdb) ", repeat=1, + annotation_suffix=0x4d6b44 "prompt") at top.c:2091 +#8 0x4d6cf0 in command_loop () at top.c:1345 +#9 0x4e25bc in main (argc=1, argv=0x7ffffdf4) at main.c:635 + + +LDD +=== +This is a program which lists the shared libraries which a library needs. +e.g. + ldd ./gdb +outputs +libncurses.so.4 => /usr/lib/libncurses.so.4 (0x40018000) +libm.so.6 => /lib/libm.so.6 (0x4005e000) +libc.so.6 => /lib/libc.so.6 (0x40084000) +/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) + +Debugging modules +================= +As modules are dynamically loaded into the kernel their address can be +anywhere to get around this use the -m option with insmod to emit a load +map which can be piped into a file if required. + +The proc file system +==================== +What is it ?. +It is a filesystem created by the kernel with files which are created on demand +by the kernel if read, or can be used to modify kernel parameters, +it is a powerful concept. + +e.g. + +cat /proc/sys/net/ipv4/ip_forward +On my machine outputs +0 +telling me ip_forwarding is not on to switch it on I can do +echo 1 > /proc/sys/net/ipv4/ip_forward +cat it again +cat /proc/sys/net/ipv4/ip_forward +On my machine now outputs +1 +IP forwarding is on. +There is a lot of useful info in here best found by going in & having a look around, +so I'll take you through some entries I consider important. + +All the processes running on the machine have there own entry defined by +/proc/ +So lets have a look at the init process +cd /proc/1 + +cat cmdline +emits +init [2] + +cd /proc/1/fd +This contains numerical entries of all the open files, +some of these you can cat e.g. stdout (2) + +cat /proc/29/maps +on my machine emits + +00400000-00478000 r-xp 00000000 5f:00 4103 /bin/bash +00478000-0047e000 rw-p 00077000 5f:00 4103 /bin/bash +0047e000-00492000 rwxp 00000000 00:00 0 +40000000-40015000 r-xp 00000000 5f:00 14382 /lib/ld-2.1.2.so +40015000-40016000 rw-p 00014000 5f:00 14382 /lib/ld-2.1.2.so +40016000-40017000 rwxp 00000000 00:00 0 +40017000-40018000 rw-p 00000000 00:00 0 +40018000-4001b000 r-xp 00000000 5f:00 14435 /lib/libtermcap.so.2.0.8 +4001b000-4001c000 rw-p 00002000 5f:00 14435 /lib/libtermcap.so.2.0.8 +4001c000-4010d000 r-xp 00000000 5f:00 14387 /lib/libc-2.1.2.so +4010d000-40111000 rw-p 000f0000 5f:00 14387 /lib/libc-2.1.2.so +40111000-40114000 rw-p 00000000 00:00 0 +40114000-4011e000 r-xp 00000000 5f:00 14408 /lib/libnss_files-2.1.2.so +4011e000-4011f000 rw-p 00009000 5f:00 14408 /lib/libnss_files-2.1.2.so +7fffd000-80000000 rwxp ffffe000 00:00 0 + + +Showing us the shared libraries init uses where they are in memory +& memory access permissions for each virtual memory area. + +/proc/1/cwd is a softlink to the current working directory. +/proc/1/root is the root of the filesystem for this process. + +/proc/1/mem is the current running processes memory which you +can read & write to like a file. +strace uses this sometimes as it is a bit faster than the +rather inefficent ptrace interface for peeking at DATA. + + +cat status + +Name: init +State: S (sleeping) +Pid: 1 +PPid: 0 +Uid: 0 0 0 0 +Gid: 0 0 0 0 +Groups: +VmSize: 408 kB +VmLck: 0 kB +VmRSS: 208 kB +VmData: 24 kB +VmStk: 8 kB +VmExe: 368 kB +VmLib: 0 kB +SigPnd: 0000000000000000 +SigBlk: 0000000000000000 +SigIgn: 7fffffffd7f0d8fc +SigCgt: 00000000280b2603 +CapInh: 00000000fffffeff +CapPrm: 00000000ffffffff +CapEff: 00000000fffffeff + +User PSW: 070de000 80414146 +task: 004b6000 tss: 004b62d8 ksp: 004b7ca8 pt_regs: 004b7f68 +User GPRS: +00000400 00000000 0000000b 7ffffa90 +00000000 00000000 00000000 0045d9f4 +0045cafc 7ffffa90 7fffff18 0045cb08 +00010400 804039e8 80403af8 7ffff8b0 +User ACRS: +00000000 00000000 00000000 00000000 +00000001 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +Kernel BackChain CallChain BackChain CallChain + 004b7ca8 8002bd0c 004b7d18 8002b92c + 004b7db8 8005cd50 004b7e38 8005d12a + 004b7f08 80019114 +Showing among other things memory usage & status of some signals & +the processes'es registers from the kernel task_structure +as well as a backchain which may be useful if a process crashes +in the kernel for some unknown reason. + +Starting points for debugging scripting languages etc. +====================================================== + +bash/sh + +bash -x +e.g. bash -x /usr/bin/bashbug +displays the following lines as it executes them. ++ MACHINE=i586 ++ OS=linux-gnu ++ CC=gcc ++ CFLAGS= -DPROGRAM='bash' -DHOSTTYPE='i586' -DOSTYPE='linux-gnu' -DMACHTYPE='i586-pc-linux-gnu' -DSHELL -DHAVE_CONFIG_H -I. -I. -I./lib -O2 -pipe ++ RELEASE=2.01 ++ PATCHLEVEL=1 ++ RELSTATUS=release ++ MACHTYPE=i586-pc-linux-gnu + +perl -d runs the perlscript in a fully intercative debugger +. +Type 'h' in the debugger for help. + +for debugging java type +jdb another fully interactive gdb style debugger. +& type ? in the debugger for help. + + +References: +----------- +Enterprise Systems Architecture Reference Summary +Enterprise Systems Architecture Principles of Operation +Hartmut Penners 390 stack frame sheet. +IBM Mainframe Channel Attachment a technology brief from a CISCO webpage +Various bits of man & info pages of Linux. +Linux & GDB source. +Various info & man pages. +CMS Help on tracing commands. + diff --git a/Documentation/magic-number.txt b/Documentation/magic-number.txt index 4449732ab81a..5b81ca3f863c 100644 --- a/Documentation/magic-number.txt +++ b/Documentation/magic-number.txt @@ -48,7 +48,7 @@ Magic Name Number Structure File PG_MAGIC 'P' pg_{read,write}_hdr include/linux/pg.h MKISS_DRIVER_MAGIC 0x04bf mkiss_channel drivers/net/mkiss.h RISCOM8_MAGIC 0x0907 riscom_port drivers/char/riscom8.h -APM_BIOS_MAGIC 0x4101 apm_bios_struct include/linux/apm_bios.h +APM_BIOS_MAGIC 0x4101 apm_user arch/i386/kernel/apm.c CYCLADES_MAGIC 0x4359 cyclades_port include/linux/cyclades.h FASYNC_MAGIC 0x4601 fasync_struct include/linux/fs.h PTY_MAGIC 0x5001 (none at the moment) diff --git a/Documentation/networking/sk98lin.txt b/Documentation/networking/sk98lin.txt index 863090170bce..510d2c4ef82f 100644 --- a/Documentation/networking/sk98lin.txt +++ b/Documentation/networking/sk98lin.txt @@ -3,7 +3,7 @@ sk98lin.txt created 11-Nov-1999 -Readme File for sk98lin.o v3.02 +Readme File for sk98lin.o v3.04 SK-NET Gigabit Ethernet Adapter SK-98xx Driver for Linux This file contains @@ -24,8 +24,8 @@ This file contains ============ The sk98lin driver supports the SysKonnect SK-NET Gigabit Ethernet -Adapter SK-98xx family on Linux 2.2.x. -It has been tested with Linux on Intel/x86 and ALPHA machines. +Adapter SK-98xx family on Linux 2.2.x and above. +It has been tested with Linux on Intel/x86, ALPHA and UltraSPARC machines. From v3.02 on, the driver is integrated in the linux kernel source. *** @@ -132,7 +132,8 @@ Parameters can be set at the command line while loading the module with 'insmod'. The configuration tools of some distributions can also give parameters to the driver module. If you use the kernel module loader, you can set driver parameters -in the file /etc/conf.modules. Insert a line of the form: +in the file /etc/modules.conf (or old name: /etc/conf.modules). +Insert a line of the form: options sk98lin ... @@ -281,14 +282,12 @@ The setting must be done on all adapters that can be reached by the large frames. If one adapter is not set to receive large frames, it will simply drop them. -NOTE: If you look at the statistics (with netstat) in large frame - mode while there is traffic on the net, you will see the - RX error counter go up. This is because the adapter hardware - counts received large frames as errors, although they are - received correctly. So ignore this counter in that case. - You can switch back to the standard ethernet frame size with: ifconfig eth0 mtu 1500 + +To make this setting persitent, add a script with the 'ifconfig' +line to the system startup sequence (named something like "S99sk98lin" +in /etc/rc.d/rc2.d). *** @@ -374,15 +373,27 @@ following information is available: (8) HISTORY =========== -VERSION 3.02 +VERSION 3.04 (In-Kernel version) +Problems fixed: +- Driver start failed on UltraSPARC +- Rx checksum calculation for big endian machines did not work +- Jumbo frames were counted as input-errors in netstat + +VERSION 3.03 (Standalone version) +Problems fixed: +- Compilation did not find script "printver.sh" if "." not in PATH +Known limitations: +- None + +VERSION 3.02 (In-Kernel version) Problems fixed: - None New Features: -- Integration in linux kernel source. +- Integration in Linux kernel source (2.2.14 and 2.3.29) Known limitations: - None -VERSION 3.02 +VERSION 3.01 Problems fixed: - None New Features: diff --git a/Documentation/networking/tlan.txt b/Documentation/networking/tlan.txt index 4114be27a937..83db8ef5a57f 100644 --- a/Documentation/networking/tlan.txt +++ b/Documentation/networking/tlan.txt @@ -1,25 +1,4 @@ - - -I haven't had any time to do anything for a long time, and this isn't -likely to change. So there's a driver here for anyone looking to -carry forward a project :) - -For those who are looking for help, I can't. I haven't looked at -a kernel since the early 2.0 series, so I won't know what's going on. -Your best chance at help would be joining the TLAN mailing list and -posting your question there. - -You can join by sending "subscribe tlan" in the body of an email to -majordomo@vuser.vu.union.edu. - -Thanks to those who have (and who will ;) put work in to keep the TLAN -driver working as the kernel moves on. - -James -james@sovereign.org - - -TLAN driver for Linux, version 1.0 +TLAN driver for Linux, version 1.2 README @@ -57,43 +36,7 @@ I. Supported Devices. but I do not expect any problems. -II. Building the Driver. - - The TLAN driver may be compiled into the kernel, or it may be compiled - as a module separately, or in the kernel. A patch is included for - 2.0.29 (which also works for 2.0.30, 2.0.31, and 2.0.32). - - To compile it as part of the kernel: - 1. Download and untar the TLAN driver package. - 2. If your kernel is 2.1.45 or later, you do not need to patch the - kernel sources. Copy the tlan.c and tlan.h to drivers/net in - the kernel source tree. - 3. Otherwise, apply the appropriate patch for your kernel. For - example: - - cd /usr/src/linux - patch -p1 < kernel.2.0.29 - - 4. Copy the files tlan.c and tlan.h from the TLAN package to the - directory drivers/net in the Linux kernel source tree. - 5. Configure your kernel for the TLAN driver. Answer 'Y' when - prompted to ask about experimental code (the first question). - Then answer 'Y' when prompted if to include TI ThunderLAN - support. If you want the driver compiled as a module, answer 'M' - instead of 'Y'. - 6. Make the kernel and, if necessary, the modules. - - To compile the TLAN driver independently: - 1. Download and untar the TLAN driver package. - 2. Change to the tlan directory. - 3. If you are NOT using a versioned kernel (ie, want an non- - versioned module), edit the Makefile, and comment out the - line: - MODVERSIONS = -DMODVERSIONS - 4. Run 'make'. - - -III. Driver Options +II. Driver Options 1. You can append debug=x to the end of the insmod line to get debug messages, where x is a bit field where the bits mean the following: @@ -110,15 +53,15 @@ III. Driver Options device that does not have an AUI/BNC connector will probably cause it to not function correctly.) - 4. You can set duplex=1 to force half duplex, and duplex=2 to + 3. You can set duplex=1 to force half duplex, and duplex=2 to force full duplex. - 5. You can set speed=10 to force 10Mbs operation, and speed=100Mbs + 4. You can set speed=10 to force 10Mbs operation, and speed=100 to force 100Mbs operation. (I'm not sure what will happen if a card which only supports 10Mbs is forced into 100Mbs mode.) - 3. If the driver is built into the kernel, you can use the 3rd + 5. If the driver is built into the kernel, you can use the 3rd and 4th parameters to set aui and debug respectively. For example: @@ -136,13 +79,13 @@ III. Driver Options 0x10 = use 10BaseT 0x20 = use 100BaseTx + 6. This driver can be used as a loadable module. Se net-modules.txt + for details. -IV. Things to try if you have problems. +III. Things to try if you have problems. 1. Make sure your card's PCI id is among those listed in section I, above. - 1. Make sure routing is correct. - 2. If you are using a 2.1.x kernel, try to duplicate the - problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel. + 2. Make sure routing is correct. There is also a tlan mailing list which you can join by sending "subscribe tlan" diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index ed6b35f41e96..768a51cb1203 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -35,7 +35,7 @@ On other - If you know of the key combos for other architectures, please 'b' - Will immediately reboot the system without syncing or unmounting your disks. -'o' - Will shut your system off via APM (if configured and supported). +'o' - Will shut your system off (if configured and supported). 's' - Will attempt to sync all mounted filesystems. diff --git a/Makefile b/Makefile index 573ca0664296..c41aed233359 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 SUBLEVEL = 15 -EXTRAVERSION = pre7 +EXTRAVERSION = pre8 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 98beb6f6c957..1fe6e37fa46f 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -893,11 +893,11 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps); } -void __init -init_IRQ(void) +unsigned long __init init_IRQ(unsigned long memory) { wrent(entInt, 0); alpha_mv.init_irq(); + return memory } /* diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 332e8940d707..5992593699c3 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -414,7 +414,7 @@ out: */ #include -__initfunc(void init_IRQ(void)) +unsigned long __init init_IRQ(unsigned long memory) { extern void init_dma(void); int irq; @@ -430,4 +430,5 @@ __initfunc(void init_IRQ(void)) init_FIQ(); #endif init_dma(); + return memory; } diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index 46beaaa4171e..6ea78f521031 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -403,7 +403,11 @@ no_psmouse: int 0x15 ! ignore return code mov ax,#0x05303 ! 32 bit connect - xor bx,bx + xor ebx,ebx + xor cx,cx ! paranoia + xor dx,dx ! ... + xor esi,esi ! ... + xor di,di ! ... int 0x15 jc no_32_apm_bios ! error @@ -419,6 +423,7 @@ no_psmouse: ! mov ax,#0x05300 ! APM BIOS installation check xor bx,bx + xor cx,cx ! paranoia int 0x15 jc apm_disconnect ! error -> should not happen, tidy up diff --git a/arch/i386/config.in b/arch/i386/config.in index b066464d796e..60224f55c3bc 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -104,16 +104,16 @@ if [ "$CONFIG_PARPORT" != "n" ]; then fi bool 'Advanced Power Management BIOS support' CONFIG_APM -if [ "$CONFIG_APM" = "y" ]; then +if [ "$CONFIG_APM" != "n" ]; then bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND bool ' Enable PM at boot time' CONFIG_APM_DO_ENABLE bool ' Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE bool ' Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK - bool ' Power off on shutdown' CONFIG_APM_POWER_OFF bool ' Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS + bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF fi endmenu diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index c805e16f7535..d161b93c0b66 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * APM BIOS driver for Linux - * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com) + * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.com) * * Initial development of this driver was funded by NEC Australia P/L * and NEC Corporation @@ -33,6 +33,9 @@ * Nov 1998, Version 1.7 * Jan 1999, Version 1.8 * Jan 1999, Version 1.9 + * Oct 1999, Version 1.10 + * Nov 1999, Version 1.11 + * Jan 2000, Version 1.12 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -90,6 +93,39 @@ * Use CONFIG_SMP instead of __SMP__ * Ignore BOUNCES for three seconds. * Stephen Rothwell + * 1.10: Fix for Thinkpad return code. + * Merge 2.2 and 2.3 drivers. + * Remove APM dependencies in arch/i386/kernel/process.c + * Remove APM dependencies in drivers/char/sysrq.c + * Reset time across standby. + * Allow more inititialisation on SMP. + * Remove CONFIG_APM_POWER_OFF and make it boot time + * configurable (default on). + * Make debug only a boot time parameter (remove APM_DEBUG). + * Try to blank all devices on any error. + * 1.11: Remove APM dependencies in drivers/char/console.c + * Fix for bioses that don't zero the top part of the + * entrypoint offset (Mario Sitta ) + * (reported by Panos Katsaloulis ). + * Real mode power off patch (Walter Hofmann + * ). + * 1.12: Remove CONFIG_SMP as the compiler will optimize + * the code away anyway (smp_num_cpus == 1 in UP) + * noted by Artur Skawina . + * Make power off under SMP work again. + * Fix thinko with initial engaging of BIOS. + * Make sure power off only happens on CPU 0 + * (Paul "Rusty" Russell ). + * Do error notification to user mode if BIOS calls fail. + * Move entrypoint offset fix to ...boot/setup.S + * where it belongs (Cosmos ). + * Remove smp-power-off. SMP users must now specify + * "apm=power-off" on the kernel command line. Suggested + * by Jim Avera , modified by Alan Cox + * . + * Register the /proc/apm entry even on SMP so that + * scripts that check for it before doing power off + * work (Jim Avera ). * * APM 1.1 Reference: * @@ -106,11 +142,8 @@ * Intel Corporation, Microsoft Corporation. Advanced Power Management * (APM) BIOS Interface Specification, Revision 1.2, February 1996. * - * [This document is available from Intel at: - * http://www.intel.com/IAL/powermgm - * or Microsoft at - * http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm - * ] + * [This document is available from Microsoft at: + * http://www.microsoft.com/hwdev/busbios/amp_12.htm] */ #include @@ -122,21 +155,44 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include +#include + +/* 2.2-2.3 Compatability defines */ +#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL} +#define __setup(x, y) +#define module_init(x) +#define set_current_state(a) current->state = (a) +#define create_proc_info_entry(n, m, b, g) \ + { \ + struct proc_dir_entry *r = create_proc_entry(n, m, b); \ + if (r) r->get_info = g; \ + } EXPORT_SYMBOL(apm_register_callback); EXPORT_SYMBOL(apm_unregister_callback); extern unsigned long get_cmos_time(void); +extern void machine_real_restart(unsigned char *, int); + +extern void (*acpi_idle)(void); +extern void (*acpi_power_off)(void); +#ifdef CONFIG_MAGIC_SYSRQ +extern void (*sysrq_power_off)(void); +#endif +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) +extern int (*console_blank_hook)(int); +#endif /* * The apm_bios device is one of the misc char devices. @@ -144,51 +200,13 @@ extern unsigned long get_cmos_time(void); */ #define APM_MINOR_DEV 134 -/* Configurable options: - * - * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests. - * This is necessary on the NEC Versa M series, which generates these when - * resuming from SYSTEM SUSPEND. However, enabling this on other laptops - * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate - * USER SUSPEND is ignored -- this may prevent the APM driver from updating - * the system time on a RESUME. - * - * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of - * the specification: "When disabled, the APM BIOS does not automatically - * power manage devices, enter the Standby State, enter the Suspend State, - * or take power saving steps in response to CPU Idle calls." This driver - * will make CPU Idle calls when Linux is idle (unless this feature is - * turned off -- see below). This should always save battery power, but - * more complicated APM features will be dependent on your BIOS - * implementation. You may need to turn this option off if your computer - * hangs at boot time when using APM support, or if it beeps continuously - * instead of suspending. Turn this off if you have a NEC UltraLite Versa - * 33/C or a Toshiba T400CDT. This is off by default since most machines - * do fine without this feature. - * - * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the - * idle loop. On some machines, this can activate improved power savings, - * such as a slowed CPU clock rate, when the machine is idle. These idle - * call is made after the idle loop has run for some length of time (e.g., - * 333 mS). On some machines, this will cause a hang at boot time or - * whenever the CPU becomes idle. - * - * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some - * laptops can use this to turn of the LCD backlight when the VC screen - * blanker blanks the screen. Note that this is only used by the VC screen - * blanker, and probably won't turn off the backlight when using X11. Some - * problems have been reported when using this option with gpm (if you'd - * like to debug this, please do so). - * - * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist - * on returning multiple suspend/standby events whenever one occurs. We - * really only need one at a time, so just ignore any beyond the first. - * This is probably safe on most laptops. - * - * If you are debugging the APM support for your laptop, note that code for - * all of these options is contained in this file, so you can #define or - * #undef these on the next line to avoid recompiling the whole kernel. +/* + * See Documentation/Config.help for the configuration options. * + * Various options can be changed at boot time as follows: + * apm=on/off enable/disable APM + * [no-]debug log some debugging messages + * [no-]power-off power off on shutdown */ /* KNOWN PROBLEM MACHINES: @@ -206,11 +224,6 @@ extern unsigned long get_cmos_time(void); * P = partially usable with APM patches */ -/* - * Define to have debug messages. - */ -#undef APM_DEBUG - /* * Define to always call the APM BIOS busy routine even if the clock was * not slowed by the idle routine. @@ -255,7 +268,7 @@ extern unsigned long get_cmos_time(void); /* * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then - * ignore suspend events for this amount of time + * ignore suspend events for this amount of time after a resume */ #define BOUNCE_INTERVAL (3 * HZ) @@ -266,25 +279,32 @@ extern unsigned long get_cmos_time(void); __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where)) /* - * Forward declarations + * Maximum number of events stored */ -static void suspend(void); -static void standby(void); -static void set_time(void); - -static void check_events(void); -static void do_apm_timer(unsigned long); +#define APM_MAX_EVENTS 20 -static int do_open(struct inode *, struct file *); -static int do_release(struct inode *, struct file *); -static ssize_t do_read(struct file *, char *, size_t , loff_t *); -static unsigned int do_poll(struct file *, poll_table *); -static int do_ioctl(struct inode *, struct file *, u_int, u_long); - -static int apm_get_info(char *, char **, off_t, int, int); +/* + * The per-file APM data + */ +struct apm_user { + int magic; + struct apm_user * next; + int suser: 1; + int suspend_wait: 1; + int suspend_result; + int suspends_pending; + int standbys_pending; + int suspends_read; + int standbys_read; + int event_head; + int event_tail; + apm_event_t events[APM_MAX_EVENTS]; +}; -extern int apm_register_callback(int (*)(apm_event_t)); -extern void apm_unregister_callback(int (*)(apm_event_t)); +/* + * The magic number in apm_user + */ +#define APM_BIOS_MAGIC 0x4101 /* * Local variables @@ -293,8 +313,6 @@ static struct { unsigned long offset; unsigned short segment; } apm_bios_entry; -static int apm_enabled = 0; -static int smp_hack = 0; #ifdef CONFIG_APM_CPU_IDLE static int clock_slowed = 0; #endif @@ -313,15 +331,20 @@ static int got_clock_diff = 0; #endif static int debug = 0; static int apm_disabled = 0; +#ifdef CONFIG_SMP +static int power_off_enabled = 0; +#else +static int power_off_enabled = 1; +#endif -static struct wait_queue * process_list = NULL; -static struct apm_bios_struct * user_list = NULL; +static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); +static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); +static struct apm_user * user_list = NULL; static struct timer_list apm_timer; -static char driver_version[] = "1.9"; /* no spaces */ +static char driver_version[] = "1.12"; /* no spaces */ -#ifdef APM_DEBUG static char * apm_event_name[] = { "system standby", "system suspend", @@ -338,28 +361,6 @@ static char * apm_event_name[] = { }; #define NR_APM_EVENT_NAME \ (sizeof(apm_event_name) / sizeof(apm_event_name[0])) -#endif - -static struct file_operations apm_bios_fops = { - NULL, /* lseek */ - do_read, - NULL, /* write */ - NULL, /* readdir */ - do_poll, - do_ioctl, - NULL, /* mmap */ - do_open, - NULL, /* flush */ - do_release, - NULL, /* fsync */ - NULL /* fasync */ -}; - -static struct miscdevice apm_device = { - APM_MINOR_DEV, - "apm", - &apm_bios_fops -}; typedef struct callback_list_t { int (* callback)(apm_event_t); @@ -416,9 +417,10 @@ static const lookup_t error_table[] = { # define APM_DO_CLI #endif #ifdef APM_ZERO_SEGS +# define APM_DECL_SEGS \ + unsigned int saved_fs; unsigned int saved_gs; # define APM_DO_SAVE_SEGS \ - savesegment(fs, saved_fs); \ - savesegment(gs, saved_gs) + savesegment(fs, saved_fs); savesegment(gs, saved_gs) # define APM_DO_ZERO_SEGS \ "pushl %%ds\n\t" \ "pushl %%es\n\t" \ @@ -431,25 +433,28 @@ static const lookup_t error_table[] = { "popl %%es\n\t" \ "popl %%ds\n\t" # define APM_DO_RESTORE_SEGS \ - loadsegment(fs, saved_fs); \ - loadsegment(gs, saved_gs) + loadsegment(fs, saved_fs); loadsegment(gs, saved_gs) #else +# define APM_DECL_SEGS # define APM_DO_SAVE_SEGS # define APM_DO_ZERO_SEGS # define APM_DO_POP_SEGS # define APM_DO_RESTORE_SEGS #endif -static u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, +static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) { - unsigned int saved_fs; - unsigned int saved_gs; + APM_DECL_SEGS unsigned long flags; __save_flags(flags); APM_DO_CLI; APM_DO_SAVE_SEGS; + /* + * N.B. We do NOT need a cld after the BIOS call + * because we always save and restore the flags. + */ __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" @@ -460,7 +465,7 @@ static u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, APM_DO_POP_SEGS : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx), "=S" (*esi) - : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) + : "a" (func), "b" (ebx_in), "c" (ecx_in) : "memory", "cc"); APM_DO_RESTORE_SEGS; __restore_flags(flags); @@ -471,12 +476,10 @@ static u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, * This version only returns one value (usually an error code) */ -static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, - u32 *eax) +static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax) { u8 error; - unsigned int saved_fs; - unsigned int saved_gs; + APM_DECL_SEGS unsigned long flags; __save_flags(flags); @@ -485,6 +488,10 @@ static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, { int cx, dx, si; + /* + * N.B. We do NOT need a cld after the BIOS call + * because we always save and restore the flags. + */ __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" @@ -495,7 +502,7 @@ static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, APM_DO_POP_SEGS : "=a" (*eax), "=b" (error), "=c" (cx), "=d" (dx), "=S" (si) - : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) + : "a" (func), "b" (ebx_in), "c" (ecx_in) : "memory", "cc"); } APM_DO_RESTORE_SEGS; @@ -503,11 +510,11 @@ static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, return error; } -static int apm_driver_version(u_short *val) +static int __init apm_driver_version(u_short *val) { u32 eax; - if (apm_bios_call_simple(0x530e, 0, *val, &eax)) + if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax)) return (eax >> 8) & 0xff; *val = eax; return APM_SUCCESS; @@ -520,7 +527,8 @@ static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info) u32 ecx; u32 dummy; - if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy)) + if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx, + &dummy, &dummy)) return (eax >> 8) & 0xff; *event = ebx; if (apm_bios_info.version < 0x0102) @@ -534,58 +542,122 @@ static int set_power_state(u_short what, u_short state) { u32 eax; - if (apm_bios_call_simple(0x5307, what, state, &eax)) + if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } static int apm_set_power_state(u_short state) { - return set_power_state(0x0001, state); + return set_power_state(APM_DEVICE_ALL, state); } -void apm_power_off(void) +#ifdef CONFIG_APM_CPU_IDLE +static int apm_do_idle(void) { - /* - * smp_hack == 2 means that we would have enabled APM support - * except there is more than one processor and so most of - * the APM stuff is unsafe. We will still try power down - * because is is useful to some people and they know what - * they are doing because they booted with the smp-power-off - * kernel option. - */ - if (apm_enabled || (smp_hack == 2)) - (void) apm_set_power_state(APM_STATE_OFF); + u32 dummy; + + if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &dummy)) + return 0; + +#ifdef ALWAYS_CALL_BUSY + clock_slowed = 1; +#else + clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0; +#endif + return 1; } -#ifdef CONFIG_APM_DISPLAY_BLANK -/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ -static int apm_set_display_power_state(u_short state) +static void apm_do_busy(void) { - int error; + u32 dummy; - /* Blank the first display device */ - error = set_power_state(0x0100, state); - if (error == APM_BAD_DEVICE) - /* try to blank them all instead */ - error = set_power_state(0x01ff, state); - return error; + if (clock_slowed) { + (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy); + clock_slowed = 0; + } +} + +extern int hlt_counter; + +static void apm_cpu_idle(void) +{ + while (!current->need_resched) { + if (!boot_cpu_data.hlt_works_ok) + continue; + if (hlt_counter) + continue; + /* If there is an error calling the idle routine, + we should hlt if possible. We need to check + need_resched again because an interrupt + may have occurred in apm_do_idle(). */ + start_bh_atomic(); + if (!apm_do_idle() && !current->need_resched) + asm volatile("sti ; hlt" : : : "memory"); + end_bh_atomic(); + if (current->need_resched) + break; + schedule(); + } + apm_do_busy(); } #endif -#ifdef CONFIG_APM_DO_ENABLE -static int __init apm_enable_power_management(void) +#ifdef CONFIG_SMP +static int apm_magic(void * unused) +{ + while (1) + schedule(); +} +#endif + +static void apm_power_off(void) +{ +#ifdef CONFIG_APM_REAL_MODE_POWER_OFF + unsigned char po_bios_call[] = { + 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ + 0x8e, 0xd0, /* movw ax,ss */ + 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ + 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ + 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ + 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ + 0xcd, 0x15 /* int $0x15 */ + }; +#endif + + /* + * This may be called on an SMP machine. + */ +#ifdef CONFIG_SMP + /* Many bioses don't like being called from CPU != 0 */ + while (cpu_number_map[smp_processor_id()] != 0) { + kernel_thread(apm_magic, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); + schedule(); + } +#endif +#ifdef CONFIG_APM_REAL_MODE_POWER_OFF + machine_real_restart(po_bios_call, sizeof(po_bios_call)); +#else + (void) apm_set_power_state(APM_STATE_OFF); +#endif +} + +static int apm_enable_power_management(int enable) { u32 eax; - if (apm_bios_call_simple(0x5308, - (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, - 1, &eax)) + if ((enable == 0) && (apm_bios_info.flags & APM_BIOS_DISENGAGED)) + return APM_NOT_ENGAGED; + if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL, + enable, &eax)) return (eax >> 8) & 0xff; - apm_bios_info.flags &= ~APM_BIOS_DISABLED; + if (enable) + apm_bios_info.flags &= ~APM_BIOS_DISABLED; + else + apm_bios_info.flags |= APM_BIOS_DISABLED; return APM_SUCCESS; } -#endif static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) { @@ -595,7 +667,8 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) u32 edx; u32 dummy; - if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy)) + if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0, + &eax, &ebx, &ecx, &edx, &dummy)) return (eax >> 8) & 0xff; *status = ebx; *bat = ecx; @@ -603,6 +676,7 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) return APM_SUCCESS; } +#if 0 static int apm_get_battery_status(u_short which, u_short *status, u_short *bat, u_short *life, u_short *nbat) { @@ -620,7 +694,7 @@ static int apm_get_battery_status(u_short which, u_short *status, return apm_get_power_status(status, bat, life); } - if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, + if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi)) return (eax >> 8) & 0xff; *status = ebx; @@ -629,13 +703,23 @@ static int apm_get_battery_status(u_short which, u_short *status, *nbat = esi; return APM_SUCCESS; } +#endif -static int __init apm_engage_power_management(u_short device) +static int apm_engage_power_management(u_short device, int enable) { u32 eax; - if (apm_bios_call_simple(0x530f, device, 1, &eax)) + if ((enable == 0) && (device == APM_DEVICE_ALL) + && (apm_bios_info.flags & APM_BIOS_DISABLED)) + return APM_DISABLED; + if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax)) return (eax >> 8) & 0xff; + if (device == APM_DEVICE_ALL) { + if (enable) + apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + else + apm_bios_info.flags |= APM_BIOS_DISENGAGED; + } return APM_SUCCESS; } @@ -652,37 +736,28 @@ static void apm_error(char *str, int err) str, err); } -/* Called from console driver -- must make sure apm_enabled. */ -int apm_display_blank(void) +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) +static int apm_console_blank(int blank) { -#ifdef CONFIG_APM_DISPLAY_BLANK int error; + u_short state; - if (!apm_enabled) - return 0; - error = apm_set_display_power_state(APM_STATE_STANDBY); + state = blank ? APM_STATE_STANDBY : APM_STATE_READY; + /* Blank the first display device */ + error = set_power_state(0x100, state); + if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) { + /* try to blank them all instead */ + error = set_power_state(0x1ff, state); + if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) + /* try to blank device one instead */ + error = set_power_state(0x101, state); + } if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) return 1; - apm_error("set display standby", error); -#endif + apm_error("set display", error); return 0; } - -/* Called from console driver -- must make sure apm_enabled. */ -int apm_display_unblank(void) -{ -#ifdef CONFIG_APM_DISPLAY_BLANK - int error; - - if (!apm_enabled) - return 0; - error = apm_set_display_power_state(APM_STATE_READY); - if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) - return 1; - apm_error("set display ready", error); #endif - return 0; -} int apm_register_callback(int (*callback)(apm_event_t)) { @@ -710,23 +785,23 @@ void apm_unregister_callback(int (*callback)(apm_event_t)) kfree_s(old, sizeof(callback_list_t)); } -static int queue_empty(struct apm_bios_struct * as) +static int queue_empty(struct apm_user *as) { return as->event_head == as->event_tail; } -static apm_event_t get_queued_event(struct apm_bios_struct * as) +static apm_event_t get_queued_event(struct apm_user *as) { as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; return as->events[as->event_tail]; } -static int queue_event(apm_event_t event, struct apm_bios_struct *sender) +static void queue_event(apm_event_t event, struct apm_user *sender) { - struct apm_bios_struct * as; + struct apm_user * as; if (user_list == NULL) - return 0; + return; for (as = user_list; as != NULL; as = as->next) { if (as == sender) continue; @@ -734,10 +809,8 @@ static int queue_event(apm_event_t event, struct apm_bios_struct *sender) if (as->event_head == as->event_tail) { static int notified; - if (notified == 0) { + if (notified++ == 0) printk(KERN_ERR "apm: an event queue overflowed\n"); - notified = 1; - } as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; } as->events[as->event_head] = event; @@ -757,29 +830,26 @@ static int queue_event(apm_event_t event, struct apm_bios_struct *sender) break; } } - wake_up_interruptible(&process_list); - return 1; + wake_up_interruptible(&apm_waitqueue); } static void set_time(void) { unsigned long flags; - if (!got_clock_diff) /* Don't know time zone, can't set clock */ - return; - - save_flags(flags); - cli(); - CURRENT_TIME = get_cmos_time() + clock_cmos_diff; - restore_flags(flags); + if (got_clock_diff) { /* Must know time zone in order to set clock */ + save_flags(flags); + cli(); + CURRENT_TIME = get_cmos_time() + clock_cmos_diff; + restore_flags(flags); + } } -static void suspend(void) +static void get_time_diff(void) { +#ifndef CONFIG_APM_RTC_IS_GMT unsigned long flags; - int err; -#ifndef CONFIG_APM_RTC_IS_GMT /* * Estimate time zone so that set_time can update the clock */ @@ -790,11 +860,13 @@ static void suspend(void) got_clock_diff = 1; restore_flags(flags); #endif +} - err = apm_set_power_state(APM_STATE_SUSPEND); - if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) - apm_error("suspend", err); +static void reinit_timer(void) +{ #ifdef INIT_TIMER_AFTER_SUSPEND + unsigned long flags; + save_flags(flags); cli(); /* set the clock to 100 Hz */ @@ -806,13 +878,34 @@ static void suspend(void) udelay(10); restore_flags(flags); #endif +} + +static int suspend(void) +{ + int err; + int ret; + struct apm_user *as; + + get_time_diff(); + err = apm_set_power_state(APM_STATE_SUSPEND); + reinit_timer(); set_time(); + ret = (err == APM_SUCCESS) || (err == APM_NO_ERROR); + if (!ret) + apm_error("suspend", err); + for (as = user_list; as != NULL; as = as->next) { + as->suspend_wait = 0; + as->suspend_result = (ret ? 0 : -EIO); + } + wake_up_interruptible(&apm_suspend_waitqueue); + return err; } static void standby(void) { int err; + get_time_diff(); err = apm_set_power_state(APM_STATE_STANDBY); if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) apm_error("standby", err); @@ -837,8 +930,8 @@ static apm_event_t get_event(void) return 0; } -static void send_event(apm_event_t event, apm_event_t undo, - struct apm_bios_struct *sender) +static int send_event(apm_event_t event, apm_event_t undo, + struct apm_user *sender) { callback_list_t * call; callback_list_t * fix; @@ -849,11 +942,12 @@ static void send_event(apm_event_t event, apm_event_t undo, fix->callback(undo); if (apm_bios_info.version > 0x100) apm_set_power_state(APM_STATE_REJECT); - return; + return 0; } } queue_event(event, sender); + return 1; } static void check_events(void) @@ -865,14 +959,14 @@ static void check_events(void) #endif while ((event = get_event()) != 0) { -#ifdef APM_DEBUG - if (event <= NR_APM_EVENT_NAME) - printk(KERN_DEBUG "apm: received %s notify\n", - apm_event_name[event - 1]); - else - printk(KERN_DEBUG "apm: received unknown " - "event 0x%02x\n", event); -#endif + if (debug) { + if (event <= NR_APM_EVENT_NAME) + printk(KERN_DEBUG "apm: received %s notify\n", + apm_event_name[event - 1]); + else + printk(KERN_DEBUG "apm: received unknown " + "event 0x%02x\n", event); + } #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE if (ignore_bounce && ((jiffies - last_resume) > BOUNCE_INTERVAL)) @@ -883,12 +977,15 @@ static void check_events(void) case APM_USER_STANDBY: #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND if (waiting_for_resume) - return; - waiting_for_resume = 1; + break; #endif - send_event(event, APM_STANDBY_RESUME, NULL); - if (standbys_pending <= 0) - standby(); + if (send_event(event, APM_STANDBY_RESUME, NULL)) { +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 1; +#endif + if (standbys_pending <= 0) + standby(); + } break; case APM_USER_SUSPEND: @@ -904,12 +1001,15 @@ static void check_events(void) #endif #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND if (waiting_for_resume) - return; - waiting_for_resume = 1; + break; +#endif + if (send_event(event, APM_NORMAL_RESUME, NULL)) { +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 1; #endif - send_event(event, APM_NORMAL_RESUME, NULL); - if (suspends_pending <= 0) - suspend(); + if (suspends_pending <= 0) + (void) suspend(); + } break; case APM_NORMAL_RESUME: @@ -926,9 +1026,9 @@ static void check_events(void) send_event(event, 0, NULL); break; + case APM_CAPABILITY_CHANGE: case APM_LOW_BATTERY: case APM_POWER_STATUS_CHANGE: - case APM_CAPABILITY_CHANGE: send_event(event, 0, NULL); break; @@ -937,74 +1037,33 @@ static void check_events(void) break; case APM_CRITICAL_SUSPEND: - suspend(); + (void) suspend(); break; } } } -static void do_apm_timer(unsigned long unused) +static void apm_event_handler(unsigned long unused) { - int err; - - static int pending_count = 0; + static int pending_count = 4; + int err; - if (((standbys_pending > 0) || (suspends_pending > 0)) - && (apm_bios_info.version > 0x100) - && (pending_count-- <= 0)) { + if ((standbys_pending > 0) || (suspends_pending > 0)) { + if ((apm_bios_info.version > 0x100) && (pending_count-- < 0)) { + pending_count = 4; + err = apm_set_power_state(APM_STATE_BUSY); + if (err) + apm_error("busy", err); + } + } else pending_count = 4; - - err = apm_set_power_state(APM_STATE_BUSY); - if (err) - apm_error("busy", err); - } - - if (!(((standbys_pending > 0) || (suspends_pending > 0)) - && (apm_bios_info.version == 0x100))) - check_events(); - + check_events(); init_timer(&apm_timer); apm_timer.expires = APM_CHECK_TIMEOUT + jiffies; add_timer(&apm_timer); } -/* Called from sys_idle, must make sure apm_enabled. */ -int apm_do_idle(void) -{ -#ifdef CONFIG_APM_CPU_IDLE - u32 dummy; - - if (!apm_enabled) - return 0; - - if (apm_bios_call_simple(0x5305, 0, 0, &dummy)) - return 0; - - clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0; - return 1; -#else - return 0; -#endif -} - -/* Called from sys_idle, must make sure apm_enabled. */ -void apm_do_busy(void) -{ -#ifdef CONFIG_APM_CPU_IDLE - u32 dummy; - - if (apm_enabled -#ifndef ALWAYS_CALL_BUSY - && clock_slowed -#endif - ) { - (void) apm_bios_call_simple(0x5306, 0, 0, &dummy); - clock_slowed = 0; - } -#endif -} - -static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func) +static int check_apm_user(struct apm_user *as, const char *func) { if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { printk(KERN_ERR "apm: %s passed bad filp", func); @@ -1015,33 +1074,37 @@ static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func) static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos) { - struct apm_bios_struct * as; + struct apm_user * as; int i; apm_event_t event; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); as = fp->private_data; - if (check_apm_bios_struct(as, "read")) + if (check_apm_user(as, "read")) return -EIO; if (count < sizeof(apm_event_t)) return -EINVAL; if (queue_empty(as)) { if (fp->f_flags & O_NONBLOCK) return -EAGAIN; - add_wait_queue(&process_list, &wait); + add_wait_queue(&apm_waitqueue, &wait); repeat: - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (queue_empty(as) && !signal_pending(current)) { schedule(); goto repeat; } current->state = TASK_RUNNING; - remove_wait_queue(&process_list, &wait); + remove_wait_queue(&apm_waitqueue, &wait); } i = count; while ((i >= sizeof(event)) && !queue_empty(as)) { event = get_queued_event(as); - copy_to_user(buf, &event, sizeof(event)); + if (copy_to_user(buf, &event, sizeof(event))) { + if (i < count) + break; + return -EFAULT; + } switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: @@ -1065,12 +1128,12 @@ repeat: static unsigned int do_poll(struct file *fp, poll_table * wait) { - struct apm_bios_struct * as; + struct apm_user * as; as = fp->private_data; - if (check_apm_bios_struct(as, "select")) + if (check_apm_user(as, "poll")) return 0; - poll_wait(fp, &process_list, wait); + poll_wait(fp, &apm_waitqueue, wait); if (!queue_empty(as)) return POLLIN | POLLRDNORM; return 0; @@ -1079,10 +1142,11 @@ static unsigned int do_poll(struct file *fp, poll_table * wait) static int do_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { - struct apm_bios_struct * as; + struct apm_user * as; + DECLARE_WAITQUEUE(wait, current); as = filp->private_data; - if (check_apm_bios_struct(as, "ioctl")) + if (check_apm_user(as, "ioctl")) return -EIO; if (!as->suser) return -EPERM; @@ -1092,9 +1156,8 @@ static int do_ioctl(struct inode * inode, struct file *filp, as->standbys_read--; as->standbys_pending--; standbys_pending--; - } - else - send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as); + } else if (!send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as)) + return -EAGAIN; if (standbys_pending <= 0) standby(); break; @@ -1103,11 +1166,25 @@ static int do_ioctl(struct inode * inode, struct file *filp, as->suspends_read--; as->suspends_pending--; suspends_pending--; + } else if (!send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as)) + return -EAGAIN; + if (suspends_pending <= 0) { + if (!suspend()) + return -EIO; + } else { + as->suspend_wait = 1; + add_wait_queue(&apm_suspend_waitqueue, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if ((as->suspend_wait == 0) + || signal_pending(current)) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&apm_suspend_waitqueue, &wait); + return as->suspend_result; } - else - send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as); - if (suspends_pending <= 0) - suspend(); break; default: return -EINVAL; @@ -1117,12 +1194,12 @@ static int do_ioctl(struct inode * inode, struct file *filp, static int do_release(struct inode * inode, struct file * filp) { - struct apm_bios_struct * as; + struct apm_user * as; as = filp->private_data; - filp->private_data = NULL; - if (check_apm_bios_struct(as, "release")) + if (check_apm_user(as, "release")) return 0; + filp->private_data = NULL; if (as->standbys_pending > 0) { standbys_pending -= as->standbys_pending; if (standbys_pending <= 0) @@ -1131,12 +1208,12 @@ static int do_release(struct inode * inode, struct file * filp) if (as->suspends_pending > 0) { suspends_pending -= as->suspends_pending; if (suspends_pending <= 0) - suspend(); + (void) suspend(); } if (user_list == as) user_list = as->next; else { - struct apm_bios_struct * as1; + struct apm_user * as1; for (as1 = user_list; (as1 != NULL) && (as1->next != as); @@ -1153,9 +1230,9 @@ static int do_release(struct inode * inode, struct file * filp) static int do_open(struct inode * inode, struct file * filp) { - struct apm_bios_struct * as; + struct apm_user * as; - as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL); + as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL); if (as == NULL) { printk(KERN_ERR "apm: cannot allocate struct of size %d bytes", sizeof(*as)); @@ -1179,13 +1256,12 @@ static int do_open(struct inode * inode, struct file * filp) return 0; } -int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) +static int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) { char * p; unsigned short bx; unsigned short cx; unsigned short dx; - unsigned short nbat; unsigned short error; unsigned short ac_line_status = 0xff; unsigned short battery_status = 0xff; @@ -1194,11 +1270,10 @@ int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) int time_units = -1; char *units = "?"; - if (!apm_enabled) - return 0; p = buf; - if (!(error = apm_get_power_status(&bx, &cx, &dx))) { + if ((smp_num_cpus == 1) && + !(error = apm_get_power_status(&bx, &cx, &dx))) { ac_line_status = (bx >> 8) & 0xff; battery_status = bx & 0xff; if ((cx & 0xff) != 0xff) @@ -1216,7 +1291,7 @@ int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) from the Get Power Status (0x0a) call unless otherwise noted. 0) Linux driver version (this will change if format changes) - 1) APM BIOS Version. Usually 1.0 or 1.1. + 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. 2) APM flags from APM Installation Check (0x00): bit 0: APM_16_BIT_SUPPORT bit 1: APM_32_BIT_SUPPORT @@ -1226,13 +1301,14 @@ int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) 3) AC line status 0x00: Off-line 0x01: On-line - 0x02: On backup power (APM BIOS 1.1 only) + 0x02: On backup power (BIOS >= 1.1 only) 0xff: Unknown 4) Battery status 0x00: High 0x01: Low 0x02: Critical 0x03: Charging + 0x04: Selected battery not present (BIOS >= 1.2 only) 0xff: Unknown 5) Battery flag bit 0: High @@ -1264,6 +1340,106 @@ int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) return p - buf; } +static int __init apm(void *unused) +{ + unsigned short bx; + unsigned short cx; + unsigned short dx; + unsigned short error; + char * power_stat; + char * bat_stat; + + if (apm_bios_info.version > 0x100) { + /* + * We only support BIOSs up to version 1.2 + */ + if (apm_bios_info.version > 0x0102) + apm_bios_info.version = 0x0102; + if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) { + /* Fall back to an APM 1.0 connection. */ + apm_bios_info.version = 0x100; + } + } + if (debug && (smp_num_cpus == 1)) { + printk(KERN_INFO "apm: Connection version %d.%d\n", + (apm_bios_info.version >> 8) & 0xff, + apm_bios_info.version & 0xff); + + error = apm_get_power_status(&bx, &cx, &dx); + if (error) + printk(KERN_INFO "apm: power status not available\n"); + else { + switch ((bx >> 8) & 0xff) { + case 0: power_stat = "off line"; break; + case 1: power_stat = "on line"; break; + case 2: power_stat = "on backup power"; break; + default: power_stat = "unknown"; break; + } + switch (bx & 0xff) { + case 0: bat_stat = "high"; break; + case 1: bat_stat = "low"; break; + case 2: bat_stat = "critical"; break; + case 3: bat_stat = "charging"; break; + default: bat_stat = "unknown"; break; + } + printk(KERN_INFO + "apm: AC %s, battery status %s, battery life ", + power_stat, bat_stat); + if ((cx & 0xff) == 0xff) + printk("unknown\n"); + else + printk("%d%%\n", cx & 0xff); + if (apm_bios_info.version > 0x100) { + printk(KERN_INFO + "apm: battery flag 0x%02x, battery life ", + (cx >> 8) & 0xff); + if (dx == 0xffff) + printk("unknown\n"); + else + printk("%d %s\n", dx & 0x7fff, + (dx & 0x8000) ? + "minutes" : "seconds"); + } + } + } + +#ifdef CONFIG_APM_DO_ENABLE + if (apm_bios_info.flags & APM_BIOS_DISABLED) { + /* + * This call causes my NEC UltraLite Versa 33/C to hang if it + * is booted with PM disabled but not in the docking station. + * Unfortunate ... + */ + error = apm_enable_power_management(1); + if (error) { + apm_error("enable power management", error); + return -1; + } + } +#endif + if ((apm_bios_info.flags & APM_BIOS_DISENGAGED) + && (apm_bios_info.version > 0x0100)) { + error = apm_engage_power_management(APM_DEVICE_ALL, 1); + if (error) { + apm_error("engage power management", error); + return -1; + } + } + + /* Install our power off handler.. */ + if (power_off_enabled) + acpi_power_off = apm_power_off; +#ifdef CONFIG_MAGIC_SYSRQ + sysrq_power_off = apm_power_off; +#endif +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) + if (smp_num_cpus == 1) + console_blank_hook = apm_console_blank; +#endif + + return 0; +} + void __init apm_setup(char *str, int *dummy) { int invert; @@ -1278,27 +1454,37 @@ void __init apm_setup(char *str, int *dummy) str += 3; if (strncmp(str, "debug", 5) == 0) debug = !invert; - if (strncmp(str, "smp-power-off", 13) == 0) - smp_hack = !invert; + if (strncmp(str, "power-off", 9) == 0) + power_off_enabled = !invert; str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); } } -void __init apm_bios_init(void) -{ - unsigned short bx; - unsigned short cx; - unsigned short dx; - unsigned short error; - char * power_stat; - char * bat_stat; - static struct proc_dir_entry *ent; +__setup("apm=", apm_setup); + +static struct file_operations apm_bios_fops = { + read: do_read, + poll: do_poll, + ioctl: do_ioctl, + open: do_open, + release: do_release, +}; +static struct miscdevice apm_device = { + APM_MINOR_DEV, + "apm", + &apm_bios_fops +}; + +#define APM_INIT_ERROR_RETURN return + +void __init apm_init(void) +{ if (apm_bios_info.version == 0) { printk(KERN_INFO "apm: BIOS not found.\n"); - return; + APM_INIT_ERROR_RETURN; } printk(KERN_INFO "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n", @@ -1308,7 +1494,7 @@ void __init apm_bios_init(void) driver_version); if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) { printk(KERN_INFO "apm: no 32 bit BIOS support\n"); - return; + APM_INIT_ERROR_RETURN; } /* @@ -1337,7 +1523,11 @@ void __init apm_bios_init(void) if (apm_disabled) { printk(KERN_NOTICE "apm: disabled on user request.\n"); - return; + APM_INIT_ERROR_RETURN; + } + if ((smp_num_cpus > 1) && !power_off_enabled) { + printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); + APM_INIT_ERROR_RETURN; } /* @@ -1359,18 +1549,16 @@ void __init apm_bios_init(void) set_base(gdt[APM_DS >> 3], __va((unsigned long)apm_bios_info.dseg << 4)); #ifndef APM_RELAX_SEGMENTS - if (apm_bios_info.version == 0x100) + if (apm_bios_info.version == 0x100) { #endif - { /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); /* For some unknown machine. */ _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1); /* For the DEC Hinote Ultra CT475 (and others?) */ _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1); - } #ifndef APM_RELAX_SEGMENTS - else { + } else { _set_limit((char *)&gdt[APM_CS >> 3], (apm_bios_info.cseg_len - 1) & 0xffff); _set_limit((char *)&gdt[APM_CS_16 >> 3], @@ -1379,98 +1567,27 @@ void __init apm_bios_init(void) (apm_bios_info.dseg_len - 1) & 0xffff); } #endif -#ifdef CONFIG_SMP - if (smp_num_cpus > 1) { - printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); - if (smp_hack) - smp_hack = 2; - return; - } -#endif - if (apm_bios_info.version > 0x100) { - /* - * We only support BIOSs up to version 1.2 - */ - if (apm_bios_info.version > 0x0102) - apm_bios_info.version = 0x0102; - if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) { - /* Fall back to an APM 1.0 connection. */ - apm_bios_info.version = 0x100; - } - } - if (debug) { - printk(KERN_INFO "apm: Connection version %d.%d\n", - (apm_bios_info.version >> 8) & 0xff, - apm_bios_info.version & 0xff ); - error = apm_get_power_status(&bx, &cx, &dx); - if (error) - printk(KERN_INFO "apm: power status not available\n"); - else { - switch ((bx >> 8) & 0xff) { - case 0: power_stat = "off line"; break; - case 1: power_stat = "on line"; break; - case 2: power_stat = "on backup power"; break; - default: power_stat = "unknown"; break; - } - switch (bx & 0xff) { - case 0: bat_stat = "high"; break; - case 1: bat_stat = "low"; break; - case 2: bat_stat = "critical"; break; - case 3: bat_stat = "charging"; break; - default: bat_stat = "unknown"; break; - } - printk(KERN_INFO - "apm: AC %s, battery status %s, battery life ", - power_stat, bat_stat); - if ((cx & 0xff) == 0xff) - printk("unknown\n"); - else - printk("%d%%\n", cx & 0xff); - if (apm_bios_info.version > 0x100) { - printk(KERN_INFO - "apm: battery flag 0x%02x, battery life ", - (cx >> 8) & 0xff); - if (dx == 0xffff) - printk("unknown\n"); - else - printk("%d %s\n", dx & 0x7fff, - (dx & 0x8000) ? - "minutes" : "seconds"); - } - } - } + apm(0); -#ifdef CONFIG_APM_DO_ENABLE - if (apm_bios_info.flags & APM_BIOS_DISABLED) { - /* - * This call causes my NEC UltraLite Versa 33/C to hang if it - * is booted with PM disabled but not in the docking station. - * Unfortunate ... - */ - error = apm_enable_power_management(); - if (error) { - apm_error("enable power management", error); - return; - } - } -#endif - if (((apm_bios_info.flags & APM_BIOS_DISABLED) == 0) - && (apm_bios_info.version > 0x0100)) { - if (apm_engage_power_management(0x0001) == APM_SUCCESS) - apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + create_proc_info_entry("apm", 0, 0, apm_get_info); + + if (smp_num_cpus > 1) { + printk(KERN_NOTICE + "apm: disabled - APM is not SMP safe (power off active).\n"); + APM_INIT_ERROR_RETURN; } init_timer(&apm_timer); - apm_timer.function = do_apm_timer; + apm_timer.function = apm_event_handler; apm_timer.expires = APM_CHECK_TIMEOUT + jiffies; add_timer(&apm_timer); - ent = create_proc_entry("apm", 0, 0); - if (ent != NULL) - ent->get_info = apm_get_info; - misc_register(&apm_device); - apm_enabled = 1; +#ifdef CONFIG_APM_CPU_IDLE + acpi_idle = apm_cpu_idle; +#endif } + +module_init(apm_init) diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 2bc228a76956..356051b8f1a0 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -1030,6 +1030,56 @@ static void __init pcibios_fixup_peer_bridges(void) * Exceptions for specific devices. Usually work-arounds for fatal design flaws. */ +static void __init pci_fixup_peer_bus_scan(unsigned char busno, unsigned char busmax) +{ + struct pci_bus *bus; + if((busno == 0) && (busmax > 0)) + busno++; + while(busno != 0) { + pci_scan_peer_bridge(busno); + for(bus=&pci_root;bus;bus=bus->next) + if(bus->number == busno) + break; + if(bus==NULL) { + return; + } + if(bus->subordinate < busmax) + busno = bus->subordinate + 1; + else + busno = 0; + } +} + +static void __init pci_fixup_rcc(struct pci_dev *d) +{ + /* + * Find and scan busses behind RCC LE north bridge chips + */ + struct pci_bus *bus; + unsigned char busno, busmax; + pci_probe |= PCI_NO_PEER_FIXUP; + pci_read_config_byte(d, 0x44, &busno); + pci_read_config_byte(d, 0x45, &busmax); + printk("PCI: Scanning RCC HE/LE Peer Bus Bridge %02x/%02x\n", + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); + pci_fixup_peer_bus_scan(busno, busmax); +} + +static void __init pci_fixup_compaq(struct pci_dev *d) +{ + /* + * Find and scan busses behind RCC LE north bridge chips + */ + struct pci_bus *bus; + unsigned char busno, busmax; + pci_probe |= PCI_NO_PEER_FIXUP; + pci_read_config_byte(d, 0xc8, &busno); + pci_read_config_byte(d, 0xc9, &busmax); + printk("PCI: Scanning Compaq Peer Bus Bridge %02x/%02x\n", + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); + pci_fixup_peer_bus_scan(busno, busmax); +} + static void __init pci_fixup_i450nx(struct pci_dev *d) { /* @@ -1043,10 +1093,7 @@ static void __init pci_fixup_i450nx(struct pci_dev *d) pci_read_config_byte(d, reg++, &suba); pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); - if (busno) - pci_scan_peer_bridge(busno); /* Bus A */ - if (suba < subb) - pci_scan_peer_bridge(suba+1); /* Bus B */ + pci_fixup_peer_bus_scan(busno,subb); } pci_probe |= PCI_NO_PEER_FIXUP; } @@ -1071,6 +1118,9 @@ struct dev_ex { static struct dev_ex __initdata dev_ex_table[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx, "Scanning peer host bridges" }, + { PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_HE, pci_fixup_rcc, "Scanning peer host bridges" }, + { PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_LE, pci_fixup_rcc, "Scanning peer host bridges" }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq, "Scanning peer host bridges" }, { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide, "Working around UM8886BF bugs" } }; diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index a5327bfb0de2..5e6003889596 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -1064,7 +1064,7 @@ void init_ISA_irqs (void) } } -__initfunc(void init_IRQ(void)) +unsigned long __init init_IRQ(unsigned long memory) { int i; @@ -1128,6 +1128,7 @@ __initfunc(void init_IRQ(void)) setup_x86_irq(2, &irq2); setup_x86_irq(13, &irq13); #endif + return memory; } #ifdef CONFIG_X86_IO_APIC diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 4156185aa5ed..0267180290d7 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -31,9 +31,6 @@ #include #include #include -#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) -#include -#endif #include #include @@ -52,15 +49,20 @@ spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); -#ifdef CONFIG_APM -extern int apm_do_idle(void); -extern void apm_do_busy(void); -#endif - -static int hlt_counter=0; +int hlt_counter=0; #define HARD_IDLE_TIMEOUT (HZ / 3) +/* + * Powermanagement idle function, if any.. + */ +void (*acpi_idle)(void) = NULL; + +/* + * Power off function, if any + */ +void (*acpi_power_off)(void) = NULL; + void disable_hlt(void) { hlt_counter++; @@ -71,34 +73,7 @@ void enable_hlt(void) hlt_counter--; } -#ifndef __SMP__ - -static void hard_idle(void) -{ - while (!current->need_resched) { - if (boot_cpu_data.hlt_works_ok && !hlt_counter) { -#ifdef CONFIG_APM - /* If the APM BIOS is not enabled, or there - is an error calling the idle routine, we - should hlt if possible. We need to check - need_resched again because an interrupt - may have occurred in apm_do_idle(). */ - start_bh_atomic(); - if (!apm_do_idle() && !current->need_resched) - __asm__("hlt"); - end_bh_atomic(); -#else - __asm__("hlt"); -#endif - } - if (current->need_resched) - break; - schedule(); - } -#ifdef CONFIG_APM - apm_do_busy(); -#endif -} +#ifndef CONFIG_SMP /* * The idle loop on a uniprocessor i386.. @@ -116,8 +91,8 @@ static int cpu_idle(void *unused) if (work) start_idle = jiffies; - if (jiffies - start_idle > HARD_IDLE_TIMEOUT) - hard_idle(); + if (acpi_idle && (jiffies - start_idle > HARD_IDLE_TIMEOUT)) + acpi_idle(); else { if (boot_cpu_data.hlt_works_ok && !hlt_counter && !current->need_resched) __asm__("hlt"); @@ -255,7 +230,10 @@ static unsigned char real_mode_switch [] = 0x74, 0x02, /* jz f */ 0x0f, 0x08, /* invd */ 0x24, 0x10, /* f: andb $0x10,al */ - 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */ +}; +static unsigned char jump_to_bios [] = +{ 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ }; @@ -268,32 +246,13 @@ static inline void kb_wait(void) break; } -void machine_restart(char * __unused) +/* + * Switch to real mode and then execute the code + * specified by the code and length parameters. + * We assume that length will aways be less that 100! + */ +void machine_real_restart(unsigned char *code, int length) { -#if __SMP__ - /* - * turn off the IO-APIC, so we can do a clean reboot - */ - init_pic_mode(); -#endif - - if(!reboot_thru_bios) { - /* rebooting needs to touch the page at absolute addr 0 */ - *((unsigned short *)__va(0x472)) = reboot_mode; - for (;;) { - int i; - for (i=0; i<100; i++) { - kb_wait(); - udelay(50); - outb(0xfe,0x64); /* pulse reset low */ - udelay(50); - } - /* That didn't work - force a triple fault.. */ - __asm__ __volatile__("lidt %0": :"m" (no_idt)); - __asm__ __volatile__("int3"); - } - } - cli(); /* Write zero to CMOS register number 0x0f, which the BIOS POST @@ -343,8 +302,9 @@ void machine_restart(char * __unused) off paging. Copy it near the end of the first page, out of the way of BIOS variables. */ - memcpy ((void *) (0x1000 - sizeof (real_mode_switch)), + memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100), real_mode_switch, sizeof (real_mode_switch)); + memcpy ((void *) (0x1000 - 100), code, length); /* Set up the IDT for real mode. */ @@ -375,7 +335,36 @@ void machine_restart(char * __unused) __asm__ __volatile__ ("ljmp $0x0008,%0" : - : "i" ((void *) (0x1000 - sizeof (real_mode_switch)))); + : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); +} + +void machine_restart(char * __unused) +{ +#if CONFIG_SMP + /* + * turn off the IO-APIC, so we can do a clean reboot + */ + init_pic_mode(); +#endif + + if(!reboot_thru_bios) { + /* rebooting needs to touch the page at absolute addr 0 */ + *((unsigned short *)__va(0x472)) = reboot_mode; + for (;;) { + int i; + for (i=0; i<100; i++) { + kb_wait(); + udelay(50); + outb(0xfe,0x64); /* pulse reset low */ + udelay(50); + } + /* That didn't work - force a triple fault.. */ + __asm__ __volatile__("lidt %0": :"m" (no_idt)); + __asm__ __volatile__("int3"); + } + } + + machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); } void machine_halt(void) @@ -384,9 +373,8 @@ void machine_halt(void) void machine_power_off(void) { -#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) - apm_power_off(); -#endif + if (acpi_power_off) + acpi_power_off(); } @@ -432,7 +420,7 @@ void show_regs(struct pt_regs * regs) * - if you use SMP you have a beefy enough machine that * this shouldn't matter.. */ -#ifndef __SMP__ +#ifndef CONFIG_SMP #define EXTRA_TASK_STRUCT 16 static struct task_struct * task_struct_stack[EXTRA_TASK_STRUCT]; static int task_struct_stack_ptr = -1; diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 95c17244f01e..8940331e02a6 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -44,9 +44,7 @@ #include #include #include -#ifdef CONFIG_APM #include -#endif #ifdef CONFIG_BLK_DEV_RAM #include #endif @@ -84,9 +82,7 @@ unsigned int mca_pentium_flag = 0; */ struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info; -#ifdef CONFIG_APM struct apm_bios_info apm_bios_info; -#endif struct sys_desc_table_struct { unsigned short length; unsigned char table[0]; @@ -277,9 +273,7 @@ __initfunc(void setup_arch(char **cmdline_p, ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; -#ifdef CONFIG_APM apm_bios_info = APM_BIOS_INFO; -#endif if( SYS_DESC_TABLE.length != 0 ) { MCA_bus = SYS_DESC_TABLE.table[3] &0x2; machine_id = SYS_DESC_TABLE.table[0]; @@ -394,7 +388,7 @@ __initfunc(void setup_arch(char **cmdline_p, *memory_start_p = memory_start; *memory_end_p = memory_end; -#ifdef __SMP__ +#ifdef CONFIG_SMP /* * Save possible boot-time SMP configuration: */ @@ -987,7 +981,7 @@ int get_cpuinfo(char * buffer) int i, n; for(n=0; n -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define EXIT_MISUSE 1 -#define TEMPFILENAME "/tmp/ddfXXXXXX" -#define TEMPFILENAMECHARS 8 /* 8 characters are fixed in all temp filenames */ -#define IOCTL_COMMAND 'D' << 8 -#define SLASHDEV "/dev/" -#define PROC_DASD_DEVICES "/proc/dasd/devices" -#define DASD_DRIVER_NAME "dasd" -#define PROC_LINE_LENGTH 80 -#define ERR_LENGTH 80 - -#define MAX_FILELEN NAME_MAX+PATH_MAX - -#define GIVEN_DEVNO 1 -#define GIVEN_MAJOR 2 -#define GIVEN_MINOR 4 - -#define CHECK_START 1 -#define CHECK_END 2 -#define CHECK_BLKSIZE 4 -#define CHECK_ALL ~0 - -#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} -#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} - -#define CHECK_SPEC_MAX_ONCE(i,str) \ - {if (i>1) \ - ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ - "can only be specified once\n",prog_name);} - -#define PARSE_PARAM_INTO(x,param,base,str) \ - {x=(int)strtol(param,&endptr,base); \ - if (*endptr) \ - ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ - "is in invalid format\n",prog_name);} - -typedef struct { - int start_unit; - int stop_unit; - int blksize; -} format_data_t; - -char prog_name[]="dasd_format"; -char tempfilename[]=TEMPFILENAME; - -void -exit_usage(int exitcode) -{ - printf("Usage: %s [-tvV] [-s start_track] [-e end_track] \n",prog_name); - printf(" [-b blocksize] -f dev_filename | -n 390_devno\n"); - exit(exitcode); -} - -void -get_xno_from_xno(int *devno,int *major_no,int *minor_no,int mode) -{ - FILE *file; - int d,mi,ma,rc; - char line[PROC_LINE_LENGTH]; - - file=fopen(PROC_DASD_DEVICES,"r"); - if (file==NULL) - ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to open " \ - PROC_DASD_DEVICES ": %s (do you have the /proc " \ - "filesystem enabled?)\n",prog_name,strerror(errno)); - - fgets(line,sizeof(line),file); /* omit first line */ - while (fgets(line,sizeof(line),file)!=NULL) { - rc=sscanf(line,"%X%d%d",&d,&ma,&mi); - if ( (rc==3) && - !((d!=*devno)&&(mode&GIVEN_DEVNO)) && - !((ma!=*major_no)&&(mode&GIVEN_MAJOR)) && - !((mi!=*minor_no)&&(mode&GIVEN_MINOR)) ) { - *devno=d; - *major_no=ma; - *minor_no=mi; - /* yes, this is a quick exit, but the easiest way */ - fclose(file); - return; - break; - } - } - fclose(file); - - ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to find device in the /proc " \ - "filesystem (are you sure to have the right param line?)\n", - prog_name); -} - -char * -get_devname_from_devno(int devno,int verbosity) -{ - int major_no,minor_no; - int file_major,file_minor; - struct stat stat_buf; - int rc; - int found; - char *devname; - char tmpname[MAX_FILELEN]; - - DIR *dp; - struct dirent *direntp; - - /**** get minor number ****/ - get_xno_from_xno(&devno,&major_no,&minor_no,GIVEN_DEVNO); - - /**** get device file ****/ - if ((dp=opendir(SLASHDEV)) == NULL) - ERRMSG_EXIT(EXIT_FAILURE,"%s: unable to read " SLASHDEV \ - "\n",prog_name); - found=0; - while ((direntp=readdir(dp)) != NULL) { - strcpy(tmpname,SLASHDEV); - strcat(tmpname,direntp->d_name); - rc=stat(tmpname,&stat_buf); - if (!rc) { - file_major=(stat_buf.st_rdev>>8)&0xff; - file_minor=stat_buf.st_rdev&0xff; - if ((file_major==major_no) && (file_minor==minor_no)) { - found=1; - break; - } - } - } - if (found) { - devname=malloc(strlen(direntp->d_name)); - strcpy(devname,tmpname); - } - rc=closedir(dp); - if (rc<0) ERRMSG("%s: unable to close directory " SLASHDEV \ - "; continuing\n",prog_name); - if (found) - return devname; - - if (verbosity>=1) - printf("I didn't find device node in " SLASHDEV \ - "; trying to create a temporary node\n"); - - /**** get temp file and create device node *****/ - rc=mkstemp(tempfilename); - if (rc==-1) - ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to get temporary " \ - "filename: %s\n",prog_name,strerror(errno)); - close(rc); - rc=unlink(tempfilename); - - rc=mknod(tempfilename,S_IFBLK|0600,(major_no<<8)+minor_no); - if (rc) - ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to create temporary " \ - "device node %s: %s\n",prog_name,tempfilename, - strerror(errno)); - return tempfilename; -} - -char * -check_param(int mode,format_data_t data) -{ - char *s; - - if (NULL==(s=malloc(ERR_LENGTH))) - ERRMSG_EXIT(EXIT_FAILURE,"%s: not enough memory.\n",prog_name); - - if ((mode&CHECK_START)&&(data.start_unit<0)) { - strcpy(s,"start track must be greater than zero"); - goto exit; - } - if ((mode&CHECK_END)&&(data.stop_unit<-1)) { - strcpy(s,"end track must be -1 or greater than zero"); - goto exit; - } - if ((mode&CHECK_END)&&(data.start_unit>data.stop_unit)&& - (data.stop_unit!=-1)) { - strcpy(s,"end track must be higher than start track"); - goto exit; - } - - if ((mode&CHECK_BLKSIZE)&&(data.blksize<1)) { - strcpy(s,"blocksize must be a positive integer"); - goto exit; - } - if (mode&CHECK_BLKSIZE) while (data.blksize>0) { - if ((data.blksize%2)&&(data.blksize!=1)) { - strcpy(s,"blocksize must be a power of 2"); - goto exit; - } - data.blksize/=2; - } - - free(s); - return NULL; -exit: - return s; -} - -#define ASK_PRINTOUT printf("Please enter %s",output) -#define ASK_GETBUFFER fgets(buffer,sizeof(buffer),stdin) -#define ASK_SCANFORNUMBER(var) rc=sscanf(buffer,"%d%c",&var,&c) -#define ASK_COMPLAIN_FORMAT if ((rc==2)&&(c=='\n')) rc=1; \ - if (rc==-1) rc=1; /* this happens, if enter is pressed */ \ - if (rc!=1) printf(" -- wrong input, try again.\n") -#define ASK_CHECK_PARAM(mode) str=check_param(mode,params); \ - if (str!=NULL) { printf(" -- %s\n",str); rc=0; free(str); } - -format_data_t -ask_user_for_data(format_data_t params) -{ - char buffer[20]; /* should be enough for inputing track numbers */ - char c; - int i,rc; - char *str; - char output[60],o2[12]; - - i=params.start_unit; - do { - params.start_unit=i; - sprintf(output,"the start track of the range to format " \ - "[%d]: ",i); - ASK_PRINTOUT; - ASK_GETBUFFER; - ASK_SCANFORNUMBER(params.start_unit); - ASK_COMPLAIN_FORMAT; - ASK_CHECK_PARAM(CHECK_START); - } while (rc!=1); - - i=params.stop_unit; - do { - params.stop_unit=i; - sprintf(output,"the end track of the range to format ["); - if (i==-1) sprintf(o2,"END]: "); else - sprintf(o2,"%d]: ",i); - strcat(output,o2); - ASK_PRINTOUT; - ASK_GETBUFFER; - if ( (!strcasecmp(buffer,"end")) || - (!strcasecmp(buffer,"end\n")) ) { - rc=1; - params.stop_unit=-1; - } else { - ASK_SCANFORNUMBER(params.stop_unit); - ASK_COMPLAIN_FORMAT; - ASK_CHECK_PARAM(CHECK_END); - } - } while (rc!=1); - - i=params.blksize; - do { - params.blksize=i; - sprintf(output,"the blocksize of the formatting [%d]: ",i); - ASK_PRINTOUT; - ASK_GETBUFFER; - ASK_SCANFORNUMBER(params.blksize); - ASK_COMPLAIN_FORMAT; - ASK_CHECK_PARAM(CHECK_BLKSIZE); - } while (rc!=1); - - return params; -} - -void -do_format_dasd(char *dev_name,format_data_t format_params,int testmode, - int verbosity,int withoutprompt) -{ - int fd,rc; - struct stat stat_buf; - int minor_no,major_no,devno; - char inp_buffer[5]; /* to contain yes */ - - fd=open(dev_name,O_RDWR); - if (fd==-1) - ERRMSG_EXIT(EXIT_FAILURE,"%s: error opening device %s: " \ - "%s\n",prog_name,dev_name,strerror(errno)); - - if (verbosity>=1) { - } - - rc=stat(dev_name,&stat_buf); - if (rc) { - ERRMSG_EXIT(EXIT_FAILURE,"%s: error occured during stat: " \ - "%s\n",prog_name,strerror(errno)); - } else { - if (!S_ISBLK(stat_buf.st_mode)) - ERRMSG_EXIT(EXIT_FAILURE,"%s: file is not a " \ - "blockdevice.\n",prog_name); - major_no=(stat_buf.st_rdev>>8)&0xff; - minor_no=stat_buf.st_rdev&0xff; - } - - if ( ((withoutprompt)&&(verbosity>=1)) || - (!withoutprompt) ) { - get_xno_from_xno(&devno,&major_no,&minor_no, - GIVEN_MAJOR|GIVEN_MINOR); - printf("\nI am going to format the device %s in the " \ - "following way:\n",dev_name); - printf(" Device number of device : 0x%x\n",devno); - printf(" Major number of device : %u\n",major_no); - printf(" Minor number of device : %u\n",minor_no); - printf(" Start track : %d\n" \ - ,format_params.start_unit); - printf(" End track : "); - if (format_params.stop_unit==-1) - printf("last track of disk\n"); - else - printf("%d\n",format_params.stop_unit); - printf(" Blocksize : %d\n" \ - ,format_params.blksize); - if (testmode) printf("Test mode active, omitting ioctl.\n"); - } - - while (!testmode) { - if (!withoutprompt) { - printf("\n--->> ATTENTION! <<---\n"); - printf("All data in the specified range of that " \ - "device will be lost.\nType yes to continue" \ - ", no will leave the disk untouched: "); - fgets(inp_buffer,sizeof(inp_buffer),stdin); - if (strcasecmp(inp_buffer,"yes") && - strcasecmp(inp_buffer,"yes\n")) { - printf("Omitting ioctl call (disk will " \ - "NOT be formatted).\n"); - break; - } - } - - if ( !( (withoutprompt)&&(verbosity<1) )) - printf("Formatting the device. This may take a " \ - "while (get yourself a coffee).\n"); - rc=ioctl(fd,IOCTL_COMMAND,format_params); - if (rc) - ERRMSG_EXIT(EXIT_FAILURE,"%s: the dasd driver " \ - "returned with the following error " \ - "message:\n%s\n",prog_name,strerror(errno)); - printf("Finished formatting the device.\n"); - - break; - } - - rc=close(fd); - if (rc) - ERRMSG("%s: error during close: " \ - "%s; continuing.\n",prog_name,strerror(errno)); -} - - - -int main(int argc,char *argv[]) { - int verbosity; - int testmode; - int withoutprompt; - - char *dev_name; - int devno; - char *dev_filename,*devno_param_str,*range_param_str; - char *start_param_str,*end_param_str,*blksize_param_str; - - format_data_t format_params; - - int rc; - int oc; - char *endptr; - - char c1,c2,cbuffer[6]; /* should be able to contain -end plus 1 char */ - int i1,i2; - char *str; - - int start_specified,end_specified,blksize_specified; - int devfile_specified,devno_specified,range_specified; - - /******************* initialization ********************/ - - endptr=NULL; - - /* set default values */ - format_params.start_unit=0; - format_params.stop_unit=-1; - format_params.blksize=1024; - testmode=0; - verbosity=0; - withoutprompt=0; - start_specified=end_specified=blksize_specified=0; - devfile_specified=devno_specified=range_specified=0; - - /*************** parse parameters **********************/ - - /* avoid error message generated by getopt */ - opterr=0; - - while ( (oc=getopt(argc,argv,"r:s:e:b:n:f:hty?vV")) !=EOF) { - switch (oc) { - case 'y': - withoutprompt=1; - break; - - case 't': - testmode=1; - break; - - case 'v': - verbosity++; - break; - - case '?': /* fall-through */ - case ':': - exit_usage(EXIT_MISUSE); - - case 'h': - exit_usage(0); - - case 'V': - printf("%s version 0.99\n",prog_name); - exit(0); - - case 's' : - start_param_str=optarg; - start_specified++; - break; - - case 'e' : - end_param_str=optarg; - end_specified++; - break; - - case 'b' : - blksize_param_str=optarg; - blksize_specified++; - break; - - case 'n' : - devno_param_str=optarg; - devno_specified++; - break; - - case 'f' : - dev_filename=optarg; - devfile_specified++; - break; - case 'r' : - range_param_str=optarg; - range_specified++; - break; - } - } - - /* set default values */ - - /******************** checking of parameters **************/ - - /* convert range into -s and -e */ - CHECK_SPEC_MAX_ONCE(range_specified,"formatting range"); - - while (range_specified) { - start_specified++; - end_specified++; - - /* scan for 1 or 2 integers, separated by a dash */ - rc=sscanf(range_param_str,"%d%c%d%c",&i1,&c1,&i2,&c2); - if ((rc==3)&&(c1=='-')) { - format_params.start_unit=i1; - format_params.stop_unit=i2; - break; - } - if (rc==1) { - format_params.start_unit=i1; - break; - } - - /* scan for integer and -END */ - rc=sscanf(range_param_str,"%d%s",&i1,cbuffer); - if ((rc==2)&&(!strcasecmp(cbuffer,"-END"))) { - format_params.start_unit=i1; - format_params.stop_unit=-1; - break; - } - ERRMSG_EXIT(EXIT_MISUSE,"%s: specified range " \ - "is in invalid format\n",prog_name); - } - - if ((!devfile_specified)&&(!devno_specified)) - ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ - "not specified\n",prog_name); - - if ((devfile_specified+devno_specified)>1) - ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ - "can only be specified once\n",prog_name); - - if ((!start_specified)&&(!end_specified)&&(!range_specified)&& - (!blksize_specified)) { - format_params=ask_user_for_data(format_params); - } - - CHECK_SPEC_MAX_ONCE(start_specified,"start track"); - CHECK_SPEC_MAX_ONCE(end_specified,"end track"); - CHECK_SPEC_MAX_ONCE(blksize_specified,"blocksize"); - - if (devno_specified) - PARSE_PARAM_INTO(devno,devno_param_str,16,"device number"); - if (start_specified&&!range_specified) - PARSE_PARAM_INTO(format_params.start_unit,start_param_str,10, - "start track"); - if (end_specified&&!range_specified) - PARSE_PARAM_INTO(format_params.stop_unit,end_param_str,10, - "end track"); - if (blksize_specified) - PARSE_PARAM_INTO(format_params.blksize,blksize_param_str,10, - "blocksize"); - - /***********get dev_name *********************/ - dev_name=(devno_specified)? - get_devname_from_devno(devno,verbosity): - dev_filename; - - /*** range checking *********/ - str=check_param(CHECK_ALL,format_params); - if (str!=NULL) ERRMSG_EXIT(EXIT_MISUSE,"%s: %s\n",prog_name,str); - - /*************** issue the real command *****************/ - do_format_dasd(dev_name,format_params,testmode,verbosity, - withoutprompt); - - /*************** cleanup ********************************/ - if (strncmp(dev_name,TEMPFILENAME,TEMPFILENAMECHARS)==0) { - rc=unlink(dev_name); - if ((rc)&&(verbosity>=1)) - ERRMSG("%s: temporary device node %s could not be " \ - "removed: %s\n",prog_name,dev_name, - strerror(errno)); - } else { - if (devno_specified) { - /* so we have allocated space for the filename */ - free(dev_name); - } - } - - return 0; -} diff --git a/arch/s390/boot/ipleckd.S b/arch/s390/boot/ipleckd.S index 9f20a6965caf..7b77ed55e7d4 100644 --- a/arch/s390/boot/ipleckd.S +++ b/arch/s390/boot/ipleckd.S @@ -3,7 +3,7 @@ # IPL record for 3380/3390 DASD # # S390 version -# Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation +# Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation # Author(s): Holger Smolinski # # @@ -17,6 +17,11 @@ # r13: base register index to 0x0000 # r14: callers address # r15: temporary save register (we have no stack!) + +# storage layout: + +#include + .org 0 .psw: .long 0x00080000,0x80000000+_start .ccw1: .long 0x06000000,0x00001000 # Re-Read enough of bootsector to start @@ -25,23 +30,22 @@ .org 0x58 .Lextn: .long 0x000a0000,0x00000000+.Lextn .Lsvcn: .long 0x000a0000,0x00000000+.Lsvcn -.Lprgn: .long 0x000a0000,0x00000000+.Lprgn +.Lprgn: .long 0x00080000,0x00000000+.Lecs .Lmcn: .long 0x000a0000,0x00000000+.Lmcn .Lion: .long 0x00080000,0x80000000+.Lionewaddr - .org 0xd0 -.Lnull: - .long 0x00000000,0x00000000 .org 0xe0 .Llstad:.long 0x00000000,0x00000000 # sectorno + ct of bootlist .org 0xf0 # Lets start now... _start: .globl _start - l %r1,0xb8 # get IPL-subchannel from lowcore + l %r1,__LC_SUBCHANNEL_ID # get IPL-subchannel from lowcore + st %r1,__LC_IPLDEV # keep it for reipl stsch .Lrdcdata - oi .Lrdcdata+5,0x80 # enable ssch - oi .Lrdcdata+27,0x01 # enalbe concurrent sense + oi .Lrdcdata+5,0x84 # enable ssch and multipath mode +.Lecs: xi .Lrdcdata+27,0x01 # enable concurrent sense msch .Lrdcdata + xi .Lprgn,6 # restore Wait and d/a bit in PCnew PSW l %r2,.Lparm mvc 0x0(8,%r2),.Lnull # set parmarea to null lctl %c6,%c6,.Lc6 # enable all interrupts @@ -89,14 +93,12 @@ _start: .globl _start j .Lkcont .Lzeroes: lr %r2,%r3 - slr %r3,%r3 +.L001: slr %r3,%r3 icm %r3,3,.Lcountarea+6 # get blocksize -.L008: slr %r5,%r5 # no bytes to move - mvcle %r2,%r4,0(%r10) # fill zeroes to storage + slr %r5,%r5 # no bytes to move +.L008: mvcle %r2,%r4,0 # fill zeroes to storage jo .L008 # until block is filled - bctr %r4,0 # skip to next block - a %r2,.Lcountarea+6 # proceed loadaddress - jnz .Lzeroes # proceed for additional blocks + brct %r4,.L001 # skip to next block .Lkcont: ahi %r10,8 j .Lkloop @@ -143,6 +145,11 @@ _start: .globl _start lhi %r6,12 # setup r6 correct! j .L011 .L010: + clc .Lrdcdata+3(2),.L9343 + jne .L013 + lhi %r6,9 + j .L011 +.L013: lhi %r6,10 .L011: # loop for nbl times @@ -222,8 +229,6 @@ _start: .globl _start # FIXME pre-initialized data should be listed first # NULLed storage can be taken from anywhere ;) -.L3390: - .word 0x3390 .Lblklst: .long 0x00002000 .align 8 @@ -233,12 +238,17 @@ _start: .globl _start .long 0x020a0000,0x00000000+.Ltpi .Lorb: .long 0x0049504c,0x0080ff00 # intparm is " IPL" - .long 0x00000000 # CCW address .Lc6: .long 0xff000000 .Lstart: .long 0x00010000 # do not separate .Lstart and .Lparm .Lparm: .long 0x00008000 # they are loaded with a LM +.L3390: + .word 0x3390 +.L9343: + .word 0x9343 +.Lnull: + .long 0x00000000,0x00000000 .Lrdcdata: .long 0x00000000,0x00000000 .long 0x00000000,0x00000000 @@ -270,7 +280,7 @@ _start: .globl _start .long 0x06000001,0x00000000 .long 0x00000000,0x01000000 .long 0x12345678 - .org 0x3c8 + .org 0x7c8 .Lrdcccw: # CCW read device characteristics .long 0x64400040,0x00000000+.Lrdcdata .long 0x63400010,0x00000000+.Ldedata @@ -282,7 +292,7 @@ _start: .globl _start .long 0x47400010,0x00000000+.Llodata .Lrdccw: .long 0x86400000,0x00000000 - .org 0x400 + .org 0x800 # end of pre initialized data is here CCWarea follows # from here we load 1k blocklist # end of function diff --git a/arch/s390/boot/silo.c b/arch/s390/boot/silo.c deleted file mode 100644 index ae2b2cc708d9..000000000000 --- a/arch/s390/boot/silo.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * arch/s390/boot/silo.c - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Holger Smolinski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* from dasd.h */ -#define DASD_PARTN_BITS 2 -#define BIODASDRWTB _IOWR('D',5,int) -/* end */ - -#define PRINT_LEVEL(x,y...) if ( silo_options.verbosity >= x ) printf(y) -#define ERROR_LEVEL(x,y...) if ( silo_options.verbosity >= x ) fprintf(stderr,y) -#define TOGGLE(x) ((x)=((x)?(0):(1))) -#define GETARG(x) {int len=strlen(optarg);x=malloc(len);strncpy(x,optarg,len);PRINT_LEVEL(1,"%s set to %s\n",#x,optarg);} - -#define ITRY(x) if ( (x) == -1 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } -#define NTRY(x) if ( (x) == 0 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } - -#define MAX_CLUSTERS 256 -#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) - -struct silo_options - { - short int verbosity; - struct - { - unsigned char testonly; - } - flags; - char *image; - char *ipldevice; - char *parmfile; - char *ramdisk; - char *bootsect; - } -silo_options = -{ - 1, /* verbosity */ - { - 0, /* testonly */ - } - , - "./image", /* image */ - NULL, /* ipldevice */ - NULL, /* parmfile */ - NULL, /* initrd */ - "ipleckd.boot", /* bootsector */ -}; - -struct blockdesc - { - unsigned long off; - unsigned short ct; - unsigned long addr; - }; - -struct blocklist - { - struct blockdesc blk[MAX_CLUSTERS]; - unsigned short ix; - }; - -void -usage (void) -{ - printf ("Usage:\n"); - printf ("silo -d ipldevice [additional options]\n"); - printf ("-d /dev/node : set ipldevice to /dev/node\n"); - printf ("-f image : set image to image\n"); - printf ("-p parmfile : set parameter file to parmfile\n"); - printf ("-b bootsect : set bootsector to bootsect\n"); - printf ("Additional options\n"); - printf ("-v: increase verbosity level\n"); - printf ("-v#: set verbosity level to #\n"); - printf ("-t: toggle testonly flag\n"); - printf ("-h: print this message\n"); - printf ("-?: print this message\n"); -} - -int -parse_options (struct silo_options *o, int argc, char *argv[]) -{ - int rc = 0; - int oc; - if (argc == 1) - { - usage (); - exit (0); - } - while ((oc = getopt (argc, argv, "f:d:p:r:b:B:h?v::t")) != -1) - { - switch (oc) - { - case 't': - TOGGLE (o->flags.testonly); - PRINT_LEVEL (1, "Testonly flag is now %sactive\n", o->flags.testonly ? "" : "in"); - break; - case 'v': - { - unsigned short v; - if (optarg && sscanf (optarg, "%hu", &v)) - o->verbosity = v; - else - o->verbosity++; - PRINT_LEVEL (1, "Verbosity value is now %hu\n", o->verbosity); - break; - } - case 'h': - case '?': - usage (); - break; - case 'd': - GETARG (o->ipldevice); - break; - case 'f': - GETARG (o->image); - break; - case 'p': - GETARG (o->parmfile); - break; - case 'r': - GETARG (o->ramdisk); - break; - case 'b': - GETARG (o->bootsect); - break; - default: - rc = EINVAL; - break; - } - } - return rc; -} - -int -verify_device (char *name) -{ - int rc = 0; - struct stat dst; - struct stat st; - ITRY (stat (name, &dst)); - if (S_ISBLK (dst.st_mode)) - { - if (!(MINOR (dst.st_rdev) & PARTN_MASK)) - { - rc = dst.st_rdev; - } - else - /* invalid MINOR & PARTN_MASK */ - { - ERROR_LEVEL (1, "Cannot boot from partition %d %d %d", - (int) PARTN_MASK, (int) MINOR (dst.st_rdev), (int) (PARTN_MASK & MINOR (dst.st_rdev))); - rc = -1; - errno = EINVAL; - } - } - else - /* error S_ISBLK */ - { - ERROR_LEVEL (1, "%s is no block device\n", name); - rc = -1; - errno = EINVAL; - } - return rc; -} - -int -verify_file (char *name, int dev) -{ - int rc = 0; - struct stat dst; - struct stat st; - int bs = 1024; - int l; - ITRY (stat (name, &dst)); - if (S_ISREG (dst.st_mode)) - { - if ((unsigned) MAJOR (dev) == (unsigned) MAJOR (dst.st_dev) && (unsigned) MINOR (dev) == (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)) - { - /* whatever to do if all is ok... */ - } - else - /* devicenumber doesn't match */ - { - ERROR_LEVEL (1, "%s is not on device (%d/%d) but on (%d/%d)\n", name, (unsigned) MAJOR (dev), (unsigned) MINOR (dev), (unsigned) MAJOR (dst.st_dev), (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)); - rc = -1; - errno = EINVAL; - } - } - else - /* error S_ISREG */ - { - ERROR_LEVEL (1, "%s is neither regular file nor linkto one\n", name); - rc = -1; - errno = EINVAL; - } - return rc; -} - -int -verify_options (struct silo_options *o) -{ - int rc = 0; - int dev = 0; - int crc = 0; - if (!o->ipldevice || !o->image || !o->bootsect) - { - usage (); - exit (1); - } - PRINT_LEVEL (1, "IPL device is: '%s'", o->ipldevice); - - ITRY (dev = verify_device (o->ipldevice)); - PRINT_LEVEL (2, "...ok...(%d/%d)", (unsigned short) MAJOR (dev), (unsigned short) MINOR (dev)); - PRINT_LEVEL (1, "\n"); - - PRINT_LEVEL (0, "bootsector is: '%s'", o->bootsect); - ITRY (verify_file (o->bootsect, dev)); - PRINT_LEVEL (1, "...ok..."); - PRINT_LEVEL (0, "\n"); - - PRINT_LEVEL (0, "Kernel image is: '%s'", o->image); - ITRY (verify_file (o->image, dev)); - PRINT_LEVEL (1, "...ok..."); - PRINT_LEVEL (0, "\n"); - - if (o->parmfile) - { - PRINT_LEVEL (0, "parameterfile is: '%s'", o->parmfile); - ITRY (verify_file (o->parmfile, dev)); - PRINT_LEVEL (1, "...ok..."); - PRINT_LEVEL (0, "\n"); - } - - if (o->ramdisk) - { - PRINT_LEVEL (0, "initialramdisk is: '%s'", o->ramdisk); - ITRY (verify_file (o->ramdisk, dev)); - PRINT_LEVEL (1, "...ok..."); - PRINT_LEVEL (0, "\n"); - } - - return crc; -} - - -int -add_file_to_blocklist (char *name, struct blocklist *lst, long addr) -{ - int fd; - int devfd; - struct stat fst; - int i; - int blk; - int bs; - int blocks; - - int rc = 0; - - ITRY (fd = open (name, O_RDONLY)); - ITRY (fstat (fd, &fst)); - ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, fst.st_dev)); - ITRY (devfd = open ("/tmp/silodev", O_RDONLY)); - ITRY (ioctl (fd, FIGETBSZ, &bs)); - blocks = (fst.st_size + bs - 1) / bs; - for (i = 0; i < blocks; i++) - { - blk = i; - ITRY (ioctl (fd, FIBMAP, &blk)); - if (blk) - { - int oldblk = blk; - ITRY (ioctl (devfd, BIODASDRWTB, &blk)); - if (blk <= 0) - { - ERROR_LEVEL (0, "BIODASDRWTB on blk %d returned %d\n", oldblk, blk); - break; - } - } - else - { - PRINT_LEVEL (1, "Filled hole on blk %d\n", i); - } - if (lst->ix == 0 || i == 0 || - lst->blk[lst->ix - 1].ct >= 128 || - (lst->blk[lst->ix - 1].off + lst->blk[lst->ix - 1].ct != blk && - !(lst->blk[lst->ix - 1].off == 0 && blk == 0))) - { - if (lst->ix >= MAX_CLUSTERS) - { - rc = 1; - errno = ENOMEM; - break; - } - lst->blk[lst->ix].off = blk; - lst->blk[lst->ix].ct = 1; - lst->blk[lst->ix].addr = addr + i * bs; - lst->ix++; - } - else - { - lst->blk[lst->ix - 1].ct++; - } - } - ITRY(unlink("/tmp/silodev")); - return rc; -} - -int -write_bootsect (char *ipldevice, char *bootsect, struct blocklist *blklst) -{ - int i; - int s_fd, d_fd, b_fd, bd_fd; - struct stat s_st, d_st, b_st; - int rc=0; - int bs, boots; - char buffer[4096]; - ITRY (d_fd = open (ipldevice, O_RDWR | O_SYNC)); - ITRY (fstat (d_fd, &d_st)); - ITRY (s_fd = open ("boot.map", O_RDWR | O_TRUNC | O_CREAT | O_SYNC)); - ITRY (verify_file (bootsect, d_st.st_rdev)); - for (i = 0; i < blklst->ix; i++) - { - int offset = blklst->blk[i].off; - int addrct = blklst->blk[i].addr | (blklst->blk[i].ct & 0xff); - PRINT_LEVEL (1, "ix %i: offset: %06x count: %02x address: 0x%08x\n", i, offset, blklst->blk[i].ct & 0xff, blklst->blk[i].addr); - NTRY (write (s_fd, &offset, sizeof (int))); - NTRY (write (s_fd, &addrct, sizeof (int))); - } - ITRY (ioctl (s_fd,FIGETBSZ, &bs)); - ITRY (stat ("boot.map", &s_st)); - if (s_st.st_size > bs ) - { - ERROR_LEVEL (0,"boot.map is larger than one block\n"); - rc = -1; - errno = EINVAL; - } - boots=0; - ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, s_st.st_dev)); - ITRY (bd_fd = open ("/tmp/silodev", O_RDONLY)); - ITRY ( ioctl(s_fd,FIBMAP,&boots)); - ITRY (ioctl (bd_fd, BIODASDRWTB, &boots)); - PRINT_LEVEL (1, "Bootmap is in block no: 0x%08x\n", boots); - close (bd_fd); - close(s_fd); - ITRY (unlink("/tmp/silodev")); - /* Now patch the bootsector */ - ITRY (b_fd = open (bootsect, O_RDONLY)); - NTRY (read (b_fd, buffer, 4096)); - memset (buffer + 0xe0, 0, 8); - *(int *) (buffer + 0xe0) = boots; - NTRY (write (d_fd, buffer, 4096)); - NTRY (write (d_fd, buffer, 4096)); - close (b_fd); - close (d_fd); - return rc; -} - -int -do_silo (struct silo_options *o) -{ - int rc = 0; - - int device_fd; - int image_fd; - struct blocklist blklist; - memset (&blklist, 0, sizeof (struct blocklist)); - ITRY (add_file_to_blocklist (o->image, &blklist, 0x00000000)); - if (o->parmfile) - { - ITRY (add_file_to_blocklist (o->parmfile, &blklist, 0x00008000)); - } - if (o->ramdisk) - { - ITRY (add_file_to_blocklist (o->ramdisk, &blklist, 0x00800000)); - } - ITRY (write_bootsect (o->ipldevice, o->bootsect, &blklist)); - - return rc; -} - -int -main (int argct, char *argv[]) -{ - int rc = 0; - ITRY (parse_options (&silo_options, argct, argv)); - ITRY (verify_options (&silo_options)); - ITRY (do_silo (&silo_options)); - return rc; -} diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 22557160f90b..9ee7e254d431 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -52,10 +52,10 @@ CONFIG_DASD_ECKD=y # CONFIG_NETDEVICES=y CONFIG_CTC=y -# CONFIG_IUCV is not set +CONFIG_IUCV=y # CONFIG_DUMMY is not set CONFIG_NET_ETHERNET=y -# CONFIG_TR is not set +CONFIG_TR=y # # S/390 Terminal and Console options @@ -127,13 +127,15 @@ CONFIG_PROC_FS=y CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_EFS_FS is not set # # Network File Systems # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -# CONFIG_NFSD is not set +CONFIG_NFSD=y +# CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -154,4 +156,3 @@ CONFIG_LOCKD=y # # CONFIG_PROFILE is not set # CONFIG_REMOTE_DEBUG is not set -# CONFIG_MAGIC_SYSRQ is not set diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 2e7590f55729..8a0684ea80ae 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -20,9 +20,9 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o O_OBJS := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - s390fpu.o s390io.o + s390fpu.o s390io.o reipl.o -OX_OBJS := s390ksyms.o +OX_OBJS := s390_ksyms.o MX_OBJS := ifdef CONFIG_SMP diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 56e1c8c6688d..6989abf36de9 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -3,7 +3,7 @@ * S390 low-level entry points. * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), * Hartmut Penner (hp@de.ibm.com), * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), @@ -51,10 +51,14 @@ SP_CRREGS = (SP_TRAP+4) /* fpu registers are saved & restored by the gdb stub itself */ SP_FPC = (SP_CRREGS+(NUM_CRS*CR_SIZE)) SP_FPRS = (SP_FPC+FPC_SIZE+FPC_PAD_SIZE) -SP_SIZE = (SP_FPRS+(NUM_FPRS*FPR_SIZE)) +/* SP_PGM_OLD_ILC etc are not part of pt_regs & they are not + defined in ptrace.h but space is needed for this too */ +SP_PGM_OLD_ILC= (SP_FPRS+(NUM_FPRS*FPR_SIZE)) #else -SP_SIZE = (SP_TRAP+4) +SP_PGM_OLD_ILC= (SP_TRAP+4) #endif +SP_SVC_STEP = (SP_PGM_OLD_ILC+4) +SP_SIZE = (SP_SVC_STEP+4) /* * these defines are offsets into the thread_struct */ @@ -186,9 +190,6 @@ resume: lctl %c9,%c11,_TSS_PER(%r3) # Nope we didn't RES_DN1: stm %r6,%r15,24(%r15) # store resume registers of prev task - iac %r1 # get address space control bits - sacf 0 - st %r1,4(%r15) st %r15,_TSS_KSP(%r2) # store kernel stack ptr to prev->tss.ksp lhi %r0,-8192 nr %r0,%r15 @@ -204,8 +205,6 @@ RES_DN1: lctl %c7,%c7,_TSS_USERSEG(%r3) # load secondary-space for new task lctl %c13,%c13,_TSS_USERSEG(%r3) # load home-space for new task lr %r2,%r0 # return task_struct of last task - l %r1,4(%r15) - sacf 0(%r1) # set address space control bits lm %r6,%r15,24(%r15) # load resume registers of next task br %r14 @@ -228,6 +227,7 @@ sysc_lit: sysc_fork: .long sys_fork sysc_vfork: .long sys_vfork sysc_sigreturn: .long sys_sigreturn + sysc_rt_sigreturn: .long sys_rt_sigreturn sysc_execve: .long sys_execve sysc_sigsuspend: .long sys_sigsuspend sysc_rt_sigsuspend: .long sys_rt_sigsuspend @@ -235,6 +235,8 @@ sysc_lit: .globl system_call system_call: SAVE_ALL(0x20) + XC SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) +pgm_system_call: basr %r13,0 ahi %r13,sysc_lit-. # setup base pointer R13 to sysc_lit slr %r8,%r8 # gpr 8 is call save (-> tracesys) @@ -253,7 +255,6 @@ system_call: sysc_return: GET_CURRENT # load pointer to task_struct to R9 tm SP_PSW+1(%r15),0x01 # returning to user ? -# tm SP_PSW+2(%r15),0xc0 # returning to user or kernel thread ? jno sysc_leave # no-> skip bottom half, resched & signal # # check, if bottom-half has to be done @@ -272,7 +273,8 @@ sysc_return_bh: icm %r0,15,sigpending(%r9) # get sigpending from task_struct jnz sysc_signal_return sysc_leave: - + icm %r0,15,SP_SVC_STEP(%r15) # get sigpending from task_struct + jnz pgm_svcret stnsm 24(%r15),disable # disable I/O and ext. interrupts RESTORE_ALL @@ -363,17 +365,23 @@ sys_vfork_glue: sys_execve_glue: la %r2,SP_PTREGS(%r15) # load pt_regs l %r1,sysc_execve-sysc_lit(%r13) + lr %r12,%r14 # save return address basr %r14,%r1 # call sys_execve ltr %r2,%r2 # check if execve failed - jnz sysc_return-4 # it did fail -> store result in gpr2 - j sysc_return # SKIP ST 2,SP_R2(15) after BASR 14,8 - # in system_call + bnz 0(%r12) # it did fail -> store result in gpr2 + b 4(%r12) # SKIP ST 2,SP_R2(15) after BASR 14,8 + # in system_call/sysc_tracesys sys_sigreturn_glue: la %r2,SP_PTREGS(%r15) # load pt_regs as parameter l %r1,sysc_sigreturn-sysc_lit(%r13) br %r1 # branch to sys_sigreturn +sys_rt_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + l %r1,sysc_rt_sigreturn-sysc_lit(%r13) + br %r1 # branch to sys_sigreturn + # # sigsuspend and rt_sigsuspend need pt_regs as an additional # parameter and they have to skip the store of %r2 into the @@ -387,7 +395,7 @@ sys_sigsuspend_glue: lr %r3,%r2 # move history0 parameter la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter l %r1,sysc_sigsuspend-sysc_lit(%r13) - la %r14,sysc_return-sysc_lit(%r13) # skip store of return value + la %r14,4(%r14) # skip store of return value br %r1 # branch to sys_sigsuspend sys_rt_sigsuspend_glue: @@ -395,7 +403,7 @@ sys_rt_sigsuspend_glue: lr %r3,%r2 # move unewset parameter la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter l %r1,sysc_rt_sigsuspend-sysc_lit(%r13) - la %r14,sysc_return-sysc_lit(%r13) # skip store of return value + la %r14,4(%r14) # skip store of return value br %r1 # branch to sys_rt_sigsuspend .globl sys_call_table @@ -422,7 +430,7 @@ sys_call_table: .long sys_lseek .long sys_getpid /* 20 */ .long sys_mount - .long sys_umount + .long sys_oldumount .long sys_setuid .long sys_getuid .long sys_stime /* 25 */ @@ -452,7 +460,7 @@ sys_call_table: .long sys_geteuid .long sys_getegid /* 50 */ .long sys_acct - .long sys_ni_syscall /* old phys syscall holder */ + .long sys_umount .long sys_ni_syscall /* old lock syscall holder */ .long sys_ioctl .long sys_fcntl /* 55 */ @@ -573,7 +581,7 @@ sys_call_table: .long sys_setresgid /* 170 */ .long sys_getresgid .long sys_prctl - .long sys_rt_sigreturn + .long sys_rt_sigreturn_glue .long sys_rt_sigaction .long sys_rt_sigprocmask /* 175 */ .long sys_rt_sigpending @@ -591,70 +599,9 @@ sys_call_table: .long sys_ni_syscall /* streams1 */ .long sys_ni_syscall /* streams2 */ .long sys_vfork_glue /* 190 */ + .rept 254-190 .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall /* 195 */ - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall /* 205 */ - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall /* 215 */ - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall /* 225 */ - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall /* 235 */ - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall /* 245 */ - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall - .long sys_ni_syscall + .endr .long sys_msgcp /* 255 */ /* @@ -694,27 +641,19 @@ pgm_check_handler: lpsw 0x28 # it was a single stepped SVC that is causing all the trouble pgm_svcper: - st %r15,__LC_SAVE_AREA - l %r15,__LC_PGM_NEW_PSW+4 # prepare return psw - ahi %r15,pgm_svcret-pgm_check_handler - st %r15,__LC_PGM_OLD_PSW+4 # use pgm old psw as temp storage - tm __LC_SVC_OLD_PSW,0x01 # test problem state of svc old psw - jz .+8 - l %r15,__LC_KERNEL_STACK # load ksp - ahi %r15,-16 - srl %r15,3 # align stack pointer to 8 - sll %r15,3 - mvc 0(4,%r15),__LC_SAVE_AREA # save user r15 - mvc 4(4,%r15),__LC_PGM_ILC # save program check information - mvc 8(8,%r15),__LC_SVC_OLD_PSW # save return point - mvc __LC_SVC_OLD_PSW(8),__LC_PGM_OLD_PSW # move prepared return pt. - j system_call # now do the svc + SAVE_ALL(0x20) + mvi SP_SVC_STEP(%r15),1 # make SP_SVC_STEP nonzero + mvc SP_PGM_OLD_ILC(4,%r15),__LC_PGM_ILC # save program check information + j pgm_system_call # now do the svc pgm_svcret: - mvc __LC_PGM_OLD_PSW(8),8(%r15) # return point is svc return point - mvc __LC_PGM_ILC(4),4(%r15) # restore program check info - l %r15,0(%r15) # restore user r15 + mvc __LC_PGM_ILC(4),SP_PGM_OLD_ILC(%r15) # restore program check info + lhi %r0,0x28 + st %r0,SP_TRAP(%r15) # set new trap indicator + j pgm_no_sv pgm_sv: SAVE_ALL(0x28) +pgm_no_sv: + XC SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) basr %r13,0 ahi %r13,pgm_lit-. # setup base pointer R13 to $PGMDAT lh %r7,__LC_PGM_ILC # load instruction length @@ -782,7 +721,6 @@ io_int_handler: io_return: GET_CURRENT # load pointer to task_struct to R9 tm SP_PSW+1(%r15),0x01 # returning to user ? -# tm SP_PSW+2(%r15),0x80 # returning to user or kernel thread ? jz io_leave # no-> skip resched & signal stosm 24(%r15),0x03 # reenable interrupts # @@ -959,3 +897,9 @@ restart_crash: restart_go: #endif + + + + + + diff --git a/arch/s390/kernel/gdb-stub.c b/arch/s390/kernel/gdb-stub.c index 670d58f27c56..06e3adbb069b 100644 --- a/arch/s390/kernel/gdb-stub.c +++ b/arch/s390/kernel/gdb-stub.c @@ -562,7 +562,7 @@ void breakpoint(void) ".globl breakinst\n" "breakinst:\t.word %0\n\t" : - : "i" (BREAKPOINT_U16) + : "i" (S390_BREAKPOINT_U16) : ); } diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index ecac672bcc59..41c1c82f58a1 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -2,7 +2,7 @@ * arch/s390/kernel/head.S * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com), * @@ -78,6 +78,7 @@ iplstart: l %r1,0xb8 # load ipl subchannel number lhi %r2,IPL_BS # load start address bras %r14,.Lloader # load rest of ipl image + st %r1,__LC_IPLDEV # store ipl device number l %r12,.Lparm # pointer to parameter area # @@ -106,16 +107,20 @@ iplstart: bras %r14,.Lloader # load parameter file ltr %r2,%r2 # got anything ? jz .Lnopf - chi %r2,896 + chi %r2,895 jnh .Lnotrunc - lhi %r2,896 + lhi %r2,895 .Lnotrunc: l %r4,INITRD_START-PARMAREA(%r12) - clc 0(6,%r4),.Lebcdic # prefix EBCDIC in parm file ? - jne .Lnocv - ahi %r4,6 # skip EBCDIC - ahi %r2,-6 - jnp .Lnopf + la %r5,0(%r4,%r2) + lr %r3,%r2 +.Lidebc: + tm 0(%r5),0x80 # high order bit set ? + jo .Ldocv # yes -> convert from EBCDIC + ahi %r5,-1 + brct %r3,.Lidebc + j .Lnocv +.Ldocv: l %r3,.Lcvtab tr 0(256,%r4),0(%r3) # convert parameters to ascii tr 256(256,%r4),0(%r3) @@ -251,7 +256,6 @@ iplstart: .Lrdstart:.long 0x02000000 .Lldret:.long 0 .Lsnsret: .long 0 -.Lebcdic:.long 0xc5c2c3c4,0xc9c30000 .Lcvtab:.long _ebcasc # ebcdic to ascii table #endif /* CONFIG_IPL_TAPE */ @@ -289,6 +293,7 @@ iplstart: l %r1,0xb8 # load ipl subchannel number lhi %r2,0x730 # load start address bras %r14,.Lloader # load rest of ipl image + st %r1,__LC_IPLDEV # store ipl device number l %r12,.Lparm # pointer to parameter area # @@ -317,16 +322,20 @@ iplstart: bras %r14,.Lloader # load parameter file ltr %r2,%r2 # got anything ? jz .Lnopf - chi %r2,896 + chi %r2,895 jnh .Lnotrunc - lhi %r2,896 + lhi %r2,895 .Lnotrunc: l %r4,INITRD_START-PARMAREA(%r12) - clc 0(6,%r4),.Lebcdic # prefix EBCDIC in parm file ? - jne .Lnocv - ahi %r4,6 # skip EBCDIC - ahi %r2,-6 - jnp .Lnopf + la %r5,0(%r4,%r2) + lr %r3,%r2 +.Lidebc: + tm 0(%r5),0x80 # high order bit set ? + jo .Ldocv # yes -> convert from EBCDIC + ahi %r5,-1 + brct %r3,.Lidebc + j .Lnocv +.Ldocv: l %r3,.Lcvtab tr 0(256,%r4),0(%r3) # convert parameters to ascii tr 256(256,%r4),0(%r3) @@ -439,7 +448,6 @@ iplstart: .Lcr6: .long 0xff000000 .Lloadp:.long 0,0 .Lparm: .long PARMAREA -.Lebcdic:.long 0xc5c2c3c4,0xc9c30000 .L4malign0:.long 0xffc00000 .Lbigmem:.long 0x04000000 .Lrdstart:.long 0x02000000 @@ -525,7 +533,7 @@ start: basr %r13,0 # get base lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space, # virtual and never return ... .align 8 -.Lentry:.long 0x0408C000,0x80000000 + _stext +.Lentry:.long 0x04080000,0x80000000 + _stext .Lpstd: .long .Lpgd+0x7F # segment-table .Lcr0: .long 0x04b50002 .Lpcmem:.long 0x00080000,0x80000000 + .Lchkmem @@ -569,11 +577,14 @@ _stext: basr %r13,0 # get base # # Setup lowcore # + l %r1,__LC_IPLDEV # load ipl device number spx .Lprefix-.LPG2(%r13) # set prefix to linux lowcore + st %r1,__LC_IPLDEV # store ipl device number l %r15,.Linittu-.LPG2(%r13) ahi %r15,8192 # init_task_union + 8191 st %r15,__LC_KERNEL_STACK # set end of kernel stack ahi %r15,-96 + xc 0(4,%r15),0(%r15) # set backchain to zero lhi %r0,-1 st %r0,__LC_KERNEL_LEVEL # set interrupt count to -1 # diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index be046af31f0f..786e8159c8ac 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -2,16 +2,15 @@ * arch/s390/kernel/irq.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Ingo Adlung (adlung@de.ibm.com) * * Derived from "arch/i386/kernel/irq.c" * Copyright (C) 1992, 1999 Linus Torvalds, Ingo Molnar * * S/390 I/O interrupt processing and I/O request processing is - * implemented in linux/arch/s390/do_io.c + * implemented in arch/s390/kernel/s390io.c */ - #include #include #include @@ -37,10 +36,7 @@ #include #include -#include "irq.h" - - -void s390_init_IRQ(void); +unsigned long s390_init_IRQ(unsigned long); void s390_free_irq(unsigned int irq, void *dev_id); int s390_request_irq( unsigned int irq, void (*handler)(int, void *, struct pt_regs *), @@ -49,24 +45,8 @@ int s390_request_irq( unsigned int irq, void *dev_id); atomic_t nmi_counter; -spinlock_t s390_bh_lock = SPIN_LOCK_UNLOCKED; -/* - * Dummy controller type for unused interrupts - */ -int do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;} -int enable_none(unsigned int irq) { return(-ENODEV); } -int disable_none(unsigned int irq) { return(-ENODEV); } - -struct hw_interrupt_type no_irq_type = { - "none", - do_none, - enable_none, - disable_none -}; -irq_desc_t irq_desc[NR_IRQS] = { - [0 ... (NR_IRQS-1)] = { 0, &no_irq_type, }, -}; +spinlock_t s390_bh_lock = SPIN_LOCK_UNLOCKED; #if 0 /* @@ -82,9 +62,6 @@ BUILD_SMP_INTERRUPT(spurious_interrupt) #endif #if 0 -static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } -#endif - int get_irq_list(char *buf) { int i, j; @@ -92,14 +69,22 @@ int get_irq_list(char *buf) char *p = buf; p += sprintf(p, " "); + for (j=0; jirq_desc.action; + if (!action) continue; + p += sprintf(p, "%3d: ",i); #ifndef __SMP__ p += sprintf(p, "%10u ", kstat_irqs(i)); @@ -108,20 +93,27 @@ int get_irq_list(char *buf) p += sprintf(p, "%10u ", kstat.irqs[cpu_logical_map(j)][i]); #endif - p += sprintf(p, " %14s", irq_desc[i].handler->typename); + p += sprintf(p, " %14s", ioinfo[i]->irq_desc.handler->typename); p += sprintf(p, " %s", action->name); - for (action=action->next; action; action = action->next) { + for (action=action->next; action; action = action->next) + { p += sprintf(p, ", %s", action->name); - } + + } /* endfor */ + *p++ = '\n'; - } + + } /* endfor */ + p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter)); #ifdef __SMP__ p += sprintf(p, "IPI: %10u\n", atomic_read(&ipi_count)); #endif + return p - buf; } +#endif /* * Global interrupt locks for SMP. Allow interrupts to come in on any @@ -376,43 +368,48 @@ int handle_IRQ_event(unsigned int irq, int cpu, struct pt_regs * regs) int status; status = 0; - action = irq_desc[irq].action; - if (action) { + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + + action = ioinfo[irq]->irq_desc.action; + + if (action) + { status |= 1; if (!(action->flags & SA_INTERRUPT)) __sti(); - do { + do + { status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); + if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); - } + + } /* endif */ return status; } - void enable_nop(int irq) { } -__initfunc(void init_IRQ(void)) +unsigned long __init init_IRQ(unsigned long memory) { - - s390_init_IRQ(); - + return s390_init_IRQ( memory); } void free_irq(unsigned int irq, void *dev_id) { - s390_free_irq( irq, dev_id); + s390_free_irq( irq, dev_id); } @@ -422,7 +419,6 @@ int request_irq( unsigned int irq, const char *devname, void *dev_id) { - return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) ); - + return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) ); } diff --git a/arch/s390/kernel/irq.h b/arch/s390/kernel/irq.h deleted file mode 100644 index 8d37488e6f48..000000000000 --- a/arch/s390/kernel/irq.h +++ /dev/null @@ -1,723 +0,0 @@ -#ifndef __irq_h -#define __irq_h - -#include -#include - -/* - * Interrupt controller descriptor. This is all we need - * to describe about the low-level hardware. - */ -struct hw_interrupt_type { - const char *typename; - int (*handle)(unsigned int irq, - int cpu, - struct pt_regs * regs); - int (*enable) (unsigned int irq); - int (*disable)(unsigned int irq); -}; - - -/* - * Status: reason for being disabled: somebody has - * done a "disable_irq()" or we must not re-enter the - * already executing irq.. - */ -#define IRQ_INPROGRESS 1 -#define IRQ_DISABLED 2 -#define IRQ_PENDING 4 - -/* - * path management control word - */ -typedef struct { - unsigned long intparm; /* interruption parameter */ - unsigned int res0 : 2; /* reserved zeros */ - unsigned int isc : 3; /* interruption sublass */ - unsigned int res5 : 3; /* reserved zeros */ - unsigned int ena : 1; /* enabled */ - unsigned int lm : 2; /* limit mode */ - unsigned int mme : 2; /* measurement-mode enable */ - unsigned int mp : 1; /* multipath mode */ - unsigned int tf : 1; /* timing facility */ - unsigned int dnv : 1; /* device number valid */ - unsigned int dev : 16; /* device number */ - unsigned char lpm; /* logical path mask */ - unsigned char pnom; /* path not operational mask */ - unsigned char lpum; /* last path used mask */ - unsigned char pim; /* path installed mask */ - unsigned short mbi; /* measurement-block index */ - unsigned char pom; /* path operational mask */ - unsigned char pam; /* path available mask */ - unsigned char chpid[8]; /* CHPID 0-7 (if available) */ - unsigned int unused1 : 8; /* reserved zeros */ - unsigned int st : 3; /* subchannel type */ - unsigned int unused2 : 20; /* reserved zeros */ - unsigned int csense : 1; /* concurrent sense; can be enabled ...*/ - /* ... per MSCH, however, if facility */ - /* ... is not installed, this results */ - /* ... in an operand exception. */ - } pmcw_t; - -/* - * subchannel status word - */ -typedef struct { - unsigned int key : 4; /* subchannel key */ - unsigned int sctl : 1; /* suspend control */ - unsigned int eswf : 1; /* ESW format */ - unsigned int cc : 2; /* deferred condition code */ - unsigned int fmt : 1; /* format */ - unsigned int pfch : 1; /* prefetch */ - unsigned int isic : 1; /* initial-status interruption control */ - unsigned int alcc : 1; /* address-limit checking control */ - unsigned int ssi : 1; /* supress-suspended interruption */ - unsigned int zcc : 1; /* zero condition code */ - unsigned int ectl : 1; /* extended control */ - unsigned int pno : 1; /* path not operational */ - unsigned int res : 1; /* reserved */ - unsigned int fctl : 3; /* function control */ - unsigned int actl : 7; /* activity control */ - unsigned int stctl : 5; /* status control */ - unsigned long cpa; /* channel program address */ - unsigned int dstat : 8; /* device status */ - unsigned int cstat : 8; /* subchannel status */ - unsigned int count : 16; /* residual count */ - } scsw_t; - -#define SCSW_FCTL_CLEAR_FUNC 0x1 -#define SCSW_FCTL_HALT_FUNC 0x2 -#define SCSW_FCTL_START_FUNC 0x4 - -#define SCSW_ACTL_SUSPENDED 0x1 -#define SCSW_ACTL_DEVACT 0x2 -#define SCSW_ACTL_SCHACT 0x4 -#define SCSW_ACTL_CLEAR_PEND 0x8 -#define SCSW_ACTL_HALT_PEND 0x10 -#define SCSW_ACTL_START_PEND 0x20 -#define SCSW_ACTL_RESUME_PEND 0x40 - -#define SCSW_STCTL_STATUS_PEND 0x1 -#define SCSW_STCTL_SEC_STATUS 0x2 -#define SCSW_STCTL_PRIM_STATUS 0x4 -#define SCSW_STCTL_INTER_STATUS 0x8 -#define SCSW_STCTL_ALERT_STATUS 0x10 - -#define DEV_STAT_ATTENTION 0x80 -#define DEV_STAT_STAT_MOD 0x40 -#define DEV_STAT_CU_END 0x20 -#define DEV_STAT_BUSY 0x10 -#define DEV_STAT_CHN_END 0x08 -#define DEV_STAT_DEV_END 0x04 -#define DEV_STAT_UNIT_CHECK 0x02 -#define DEV_STAT_UNIT_EXCEP 0x01 - -#define SCHN_STAT_PCI 0x80 -#define SCHN_STAT_INCORR_LEN 0x40 -#define SCHN_STAT_PROG_CHECK 0x20 -#define SCHN_STAT_PROT_CHECK 0x10 -#define SCHN_STAT_CHN_DATA_CHK 0x08 -#define SCHN_STAT_CHN_CTRL_CHK 0x04 -#define SCHN_STAT_INTF_CTRL_CHK 0x02 -#define SCHN_STAT_CHAIN_CHECK 0x01 - -/* - * subchannel information block - */ -typedef struct { - pmcw_t pmcw; /* path management control word */ - scsw_t scsw; /* subchannel status word */ - char mda[12]; /* model dependent area */ - } schib_t; - -typedef struct { - char cmd_code;/* command code */ - char flags; /* flags, like IDA adressing, etc. */ - unsigned short count; /* byte count */ - void *cda; /* data address */ - } ccw1_t __attribute__ ((aligned(8))); - -#define CCW_FLAG_DC 0x80 -#define CCW_FLAG_CC 0x40 -#define CCW_FLAG_SLI 0x20 -#define CCW_FLAG_SKIP 0x10 -#define CCW_FLAG_PCI 0x08 -#define CCW_FLAG_IDA 0x04 -#define CCW_FLAG_SUSPEND 0x02 - -#define CCW_CMD_BASIC_SENSE 0x04 -#define CCW_CMD_TIC 0x08 -#define CCW_CMD_SENSE_ID 0xE4 -#define CCW_CMD_NOOP 0x03 -#define CCW_CMD_RDC 0x64 - -#define SENSE_MAX_COUNT 0x20 - -/* - * architectured values for first sense byte - */ -#define SNS0_CMD_REJECT 0x80 -#define SNS_CMD_REJECT SNS0_CMD_REJECT -#define SNS0_INTERVENTION_REQ 0x40 -#define SNS0_BUS_OUT_CHECK 0x20 -#define SNS0_EQUIPMENT_CHECK 0x10 -#define SNS0_DATA_CHECK 0x08 -#define SNS0_OVERRUN 0x04 - -/* - * operation request block - */ -typedef struct { - unsigned long intparm; /* interruption parameter */ - unsigned int key : 4; /* flags, like key, suspend control, etc. */ - unsigned int spnd : 1; /* suspend control */ - unsigned int res1 : 3; /* reserved */ - unsigned int fmt : 1; /* format control */ - unsigned int pfch : 1; /* prefetch control */ - unsigned int isic : 1; /* initial-status-interruption control */ - unsigned int alcc : 1; /* address-limit-checking control */ - unsigned int ssic : 1; /* suppress-suspended-interr. control */ - unsigned int res2 : 3; /* reserved */ - unsigned int lpm : 8; /* logical path mask */ - unsigned int ils : 1; /* incorrect length */ - unsigned int zero : 7; /* reserved zeros */ - ccw1_t *cpa; /* channel program address */ - } __attribute__ ((packed,aligned(4))) orb_t; - -typedef struct { - unsigned int res0 : 4; /* reserved */ - unsigned int pvrf : 1; /* path-verification-required flag */ - unsigned int cpt : 1; /* channel-path timeout */ - unsigned int fsavf : 1; /* Failing storage address validity flag */ - unsigned int cons : 1; /* concurrent-sense */ - unsigned int res8 : 2; /* reserved */ - unsigned int scnt : 6; /* sense count if cons == 1 */ - unsigned int res16 : 16; /* reserved */ - } erw_t; - -/* - * subchannel logout area - */ -typedef struct { - unsigned int res0 : 1; /* reserved */ - unsigned int esf : 7; /* extended status flags */ - unsigned int lpum : 8; /* last path used mask */ - unsigned int res16 : 1; /* reserved */ - unsigned int fvf : 5; /* field-validity flags */ - unsigned int sacc : 2; /* storage access code */ - unsigned int termc : 2; /* termination code */ - unsigned int devsc : 1; /* device-status check */ - unsigned int serr : 1; /* secondary error */ - unsigned int ioerr : 1; /* i/o-error alert */ - unsigned int seqc : 3; /* sequence code */ - } sublog_t ; - -/* - * Format 0 Extended Status Word (ESW) - */ -typedef struct { - sublog_t sublog; /* subchannel logout */ - erw_t erw; /* extended report word */ - void *faddr; /* failing address */ - unsigned int zeros[2]; /* 2 fullwords of zeros */ - } esw0_t; - -/* - * Format 1 Extended Status Word (ESW) - */ -typedef struct { - unsigned char zero0; /* reserved zeros */ - unsigned char lpum; /* last path used mask */ - unsigned short zero16; /* reserved zeros */ - erw_t erw; /* extended report word */ - unsigned int zeros[3]; /* 2 fullwords of zeros */ - } esw1_t; - -/* - * Format 2 Extended Status Word (ESW) - */ -typedef struct { - unsigned char zero0; /* reserved zeros */ - unsigned char lpum; /* last path used mask */ - unsigned short dcti; /* device-connect-time interval */ - erw_t erw; /* extended report word */ - unsigned int zeros[3]; /* 2 fullwords of zeros */ - } esw2_t; - -/* - * Format 3 Extended Status Word (ESW) - */ -typedef struct { - unsigned char zero0; /* reserved zeros */ - unsigned char lpum; /* last path used mask */ - unsigned short res; /* reserved */ - erw_t erw; /* extended report word */ - unsigned int zeros[3]; /* 2 fullwords of zeros */ - } esw3_t; - -typedef union { - esw0_t esw0; - esw1_t esw1; - esw2_t esw2; - esw3_t esw3; - } esw_t; - -/* - * interruption response block - */ -typedef struct { - scsw_t scsw; /* subchannel status word */ - esw_t esw; /* extended status word */ - char ecw[32]; /* extended control word */ - } irb_t __attribute__ ((aligned(4))); - -/* - * TPI info structure - */ -typedef struct { - unsigned int res : 16; /* reserved 0x00000001 */ - unsigned int irq : 16; /* aka. subchannel number */ - unsigned int intparm; /* interruption parameter */ - } tpi_info_t; - - -/* - * This is the "IRQ descriptor", which contains various information - * about the irq, including what kind of hardware handling it has, - * whether it is disabled etc etc. - * - * Pad this out to 32 bytes for cache and indexing reasons. - */ -typedef struct { - unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */ - struct hw_interrupt_type *handler; /* handle/enable/disable functions */ - struct irqaction *action; /* IRQ action list */ - unsigned int unused[3]; - spinlock_t irq_lock; - } irq_desc_t; - -// -// command information word (CIW) layout -// -typedef struct _ciw { - unsigned int et : 2; // entry type - unsigned int reserved : 2; // reserved - unsigned int ct : 4; // command type - unsigned int cmd : 8; // command - unsigned int count : 16; // count - } ciw_t; - -#define CIW_TYPE_RCD 0x0 // read configuration data -#define CIW_TYPE_SII 0x1 // set interface identifier -#define CIW_TYPE_RNI 0x2 // read node identifier - -// -// sense-id response buffer layout -// -typedef struct { - /* common part */ - unsigned char reserved; /* always 0x'FF' */ - unsigned short cu_type; /* control unit type */ - unsigned char cu_model; /* control unit model */ - unsigned short dev_type; /* device type */ - unsigned char dev_model; /* device model */ - unsigned char unused; /* padding byte */ - /* extended part */ - ciw_t ciw[62]; /* variable # of CIWs */ - } __attribute__ ((packed,aligned(4))) senseid_t; - -/* - * sense data - */ -typedef struct { - unsigned char res[32]; /* reserved */ - unsigned char data[32]; /* sense data */ - } sense_t; - -/* - * device status area, to be provided by the device driver - * when calling request_irq() as parameter "dev_id", later - * tied to the "action" control block. - * - * Note : No data area must be added after union ii or the - * effective devstat size calculation will fail ! - */ -typedef struct { - unsigned int devno; /* device number, aka. "cuu" from irb */ - unsigned int intparm; /* interrupt parameter */ - unsigned char cstat; /* channel status - accumulated */ - unsigned char dstat; /* device status - accumulated */ - unsigned char lpum; /* last path used mask from irb */ - unsigned char unused; /* not used - reserved */ - unsigned int flag; /* flag : see below */ - unsigned long cpa; /* CCW address from irb at primary status */ - unsigned int rescnt; /* res. count from irb at primary status */ - unsigned int scnt; /* sense count, if DEVSTAT_FLAG_SENSE_AVAIL */ - union { - irb_t irb; /* interruption response block */ - sense_t sense; /* sense information */ - } ii; /* interrupt information */ - } devstat_t; - -#define DEVSTAT_FLAG_SENSE_AVAIL 0x00000001 -#define DEVSTAT_NOT_OPER 0x00000002 -#define DEVSTAT_START_FUNCTION 0x00000004 -#define DEVSTAT_HALT_FUNCTION 0x00000008 -#define DEVSTAT_STATUS_PENDING 0x00000010 -#define DEVSTAT_FINAL_STATUS 0x80000000 - -/* - * Flags used as input parameters for do_IO() - */ -#define DOIO_EARLY_NOTIFICATION 0x01 /* allow for I/O completion ... */ - /* ... notification after ... */ - /* ... primary interrupt status */ -#define DOIO_RETURN_CHAN_END DOIO_EARLY_NOTIFICATION -#define DOIO_VALID_LPM 0x02 /* LPM input parameter is valid */ -#define DOIO_WAIT_FOR_INTERRUPT 0x04 /* wait synchronously for interrupt */ -#define DOIO_REPORT_ALL 0x08 /* report all interrupt conditions */ -#define DOIO_ALLOW_SUSPEND 0x10 /* allow for channel prog. suspend */ -#define DOIO_DENY_PREFETCH 0x20 /* don't allow for CCW prefetch */ -#define DOIO_SUPPRESS_INTER 0x40 /* suppress intermediate inter. */ - /* ... for suspended CCWs */ - -/* - * do_IO() - * - * Start a S/390 channel program. When the interrupt arrives - * handle_IRQ_event() is called, which eventually calls the - * IRQ handler, either immediately, delayed (dev-end missing, - * or sense required) or never (no IRQ handler registered - - * should never occur, as the IRQ (subchannel ID) should be - * disabled if no handler is present. Depending on the action - * taken, do_IO() returns : 0 - Success - * -EIO - Status pending - * see : action->dev_id->cstat - * action->dev_id->dstat - * -EBUSY - Device busy - * -ENODEV - Device not operational - */ -int do_IO( int irq, /* IRQ aka. subchannel number */ - ccw1_t *cpa, /* logical channel program address */ - unsigned long initparm, /* interruption parameter */ - unsigned char lpm, /* logical path mask */ - unsigned long flag); /* flags : see above */ - -int start_IO( int irq, /* IRQ aka. subchannel number */ - ccw1_t *cpa, /* logical channel program address */ - unsigned long intparm, /* interruption parameter */ - unsigned char lpm, /* logical path mask */ - unsigned long flag); /* flags : see above */ - -int resume_IO( int irq); /* IRQ aka. subchannel number */ - -int halt_IO( int irq, /* IRQ aka. subchannel number */ - int intparm, /* dummy intparm */ - unsigned int flag); /* possible DOIO_WAIT_FOR_INTERRUPT */ - - -int process_IRQ( struct pt_regs regs, - unsigned int irq, - unsigned int intparm); - - -int enable_cpu_sync_isc ( int irq ); -int disable_cpu_sync_isc( int irq ); - -typedef struct { - int irq; /* irq, aka. subchannel */ - unsigned int devno; /* device number */ - unsigned int status; /* device status */ - senseid_t sid_data; /* senseID data */ - } dev_info_t; - -int get_dev_info( int irq, dev_info_t *); /* to be eliminated - don't use */ - -int get_dev_info_by_irq ( int irq, dev_info_t *pdi); -int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi); - -int get_irq_by_devno( unsigned int devno ); -unsigned int get_devno_by_irq( int irq ); - -int read_dev_chars( int irq, void **buffer, int length ); -int read_conf_data( int irq, void **buffer, int *length ); - -extern senseid_t senseid[NR_IRQS]; - -extern irq_desc_t irq_desc[NR_IRQS]; - -extern int handle_IRQ_event(unsigned int, int cpu, struct pt_regs *); - -extern int set_cons_dev(int irq); -extern int reset_cons_dev(int irq); -extern int wait_cons_dev(int irq); - -/* - * Some S390 specific IO instructions as inline - */ - -extern __inline__ int stsch(int irq, volatile schib_t *addr) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "STSCH 0(%2)\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int msch(int irq, volatile schib_t *addr) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "MSCH 0(%2)\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int msch_err(int irq, volatile schib_t *addr) -{ - int ccode; - - __asm__ __volatile__( - " lr 1,%1\n" - " msch 0(%2)\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - ".section .fixup,\"ax\"\n" - "2: l %0,%3\n" - " bras 1,3f\n" - " .long 1b\n" - "3: l 1,0(1)\n" - " br 1\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,2b\n" - ".previous" - : "=d" (ccode) - : "r" (irq | 0x10000L), "a" (addr), "i" (__LC_PGM_ILC) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int tsch(int irq, volatile irb_t *addr) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "TSCH 0(%2)\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int tpi( volatile tpi_info_t *addr) -{ - int ccode; - - __asm__ __volatile__( - "TPI 0(%1)\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "a" (addr) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int ssch(int irq, volatile orb_t *addr) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "SSCH 0(%2)\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int rsch(int irq) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "RSCH\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int csch(int irq) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "CSCH\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int hsch(int irq) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - "HSCH\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "r" (irq | 0x10000L) - : "cc", "1" ); - return ccode; -} - -extern __inline__ int iac( void) -{ - int ccode; - - __asm__ __volatile__( - "IAC %0\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : : "cc"); - return ccode; -} - -typedef struct { - unsigned int vrdcdvno : 16; /* device number (input) */ - unsigned int vrdclen : 16; /* data block length (input) */ - unsigned int vrdcvcla : 8; /* virtual device class (output) */ - unsigned int vrdcvtyp : 8; /* virtual device type (output) */ - unsigned int vrdcvsta : 8; /* virtual device status (output) */ - unsigned int vrdcvfla : 8; /* virtual device flags (output) */ - unsigned int vrdcrccl : 8; /* real device class (output) */ - unsigned int vrdccrty : 8; /* real device type (output) */ - unsigned int vrdccrmd : 8; /* real device model (output) */ - unsigned int vrdccrft : 8; /* real device feature (output) */ - } __attribute__ ((packed,aligned(4))) diag210_t; - -void VM_virtual_device_info( int devno, /* device number */ - senseid_t *ps ); /* ptr to senseID data */ - -extern __inline__ int diag210( diag210_t * addr) -{ - int ccode; - - __asm__ __volatile__( - "LR 1,%1\n\t" - ".long 0x83110210\n\t" - "IPM %0\n\t" - "SRL %0,28\n\t" - : "=d" (ccode) : "a" (addr) - : "cc", "1" ); - return ccode; -} - -/* - * Various low-level irq details needed by irq.c, process.c, - * time.c, io_apic.c and smp.c - * - * Interrupt entry/exit code at both C and assembly level - */ - -void mask_irq(unsigned int irq); -void unmask_irq(unsigned int irq); - -#define MAX_IRQ_SOURCES 128 - -extern spinlock_t irq_controller_lock; - -#ifdef __SMP__ - -#include - -static inline void irq_enter(int cpu, unsigned int irq) -{ - hardirq_enter(cpu); - while (test_bit(0,&global_irq_lock)) { - eieio(); - } -} - -static inline void irq_exit(int cpu, unsigned int irq) -{ - hardirq_exit(cpu); - release_irqlock(cpu); -} - - -#else - -#define irq_enter(cpu, irq) (++local_irq_count[cpu]) -#define irq_exit(cpu, irq) (--local_irq_count[cpu]) - -#endif - -#define __STR(x) #x -#define STR(x) __STR(x) - -#ifdef __SMP__ - -/* - * SMP has a few special interrupts for IPI messages - */ - -#endif /* __SMP__ */ - -/* - * x86 profiling function, SMP safe. We might want to do this in - * assembly totally? - */ -static inline void s390_do_profile (unsigned long addr) -{ -#if 0 - if (prof_buffer && current->pid) { - addr -= (unsigned long) &_stext; - addr >>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (addr > prof_len-1) - addr = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[addr]); - } -#endif -} - -#define s390irq_spin_lock(irq) \ - spin_lock(&(irq_desc[irq].irq_lock)) - -#define s390irq_spin_unlock(irq) \ - spin_unlock(&(irq_desc[irq].irq_lock)) - -#define s390irq_spin_lock_irqsave(irq,flags) \ - spin_lock_irqsave(&(irq_desc[irq].irq_lock), flags) -#define s390irq_spin_unlock_irqrestore(irq,flags) \ - spin_unlock_irqrestore(&(irq_desc[irq].irq_lock), flags) -#endif - diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 1c3aead4f6ee..7ef50138e508 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -2,7 +2,7 @@ * arch/s390/kernel/process.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), * Hartmut Penner (hp@de.ibm.com), * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), @@ -45,7 +45,6 @@ #include #include #include -#include "irq.h" spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; @@ -163,8 +162,10 @@ int sprintf_regs(int line,char *buff,struct task_struct * task,struct thread_str { regno=(line-sp_gprs1)*4; linelen=sprintf(buff,"%08x %08x %08x %08x\n", - regs->gprs[regno], regs->gprs[regno+1], - regs->gprs[regno+2], regs->gprs[regno+3]); + regs->gprs[regno], + regs->gprs[regno+1], + regs->gprs[regno+2], + regs->gprs[regno+3]); } break; case sp_acrs: @@ -176,8 +177,10 @@ int sprintf_regs(int line,char *buff,struct task_struct * task,struct thread_str { regno=(line-sp_acrs1)*4; linelen=sprintf(buff,"%08x %08x %08x %08x\n", - regs->acrs[regno], regs->acrs[regno+1], - regs->acrs[regno+2], regs->acrs[regno+3]); + regs->acrs[regno], + regs->acrs[regno+1], + regs->acrs[regno+2], + regs->acrs[regno+3]); } break; case sp_kern_backchain: @@ -283,7 +286,13 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp, unsigned long gprs[10]; /* gprs 6 -15 */ unsigned long fprs[4]; /* fpr 4 and 6 */ unsigned long empty[4]; - struct pt_regs childregs; +#if CONFIG_REMOTE_DEBUG + gdb_pt_regs childregs; +#else + pt_regs childregs; +#endif + __u32 pgm_old_ilc; /* single step magic from entry.S */ + __u32 pgm_svc_step; } *frame; frame = (struct stack_frame *) (2*PAGE_SIZE + (unsigned long) p) -1; @@ -299,10 +308,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp, /* fake return stack for resume(), don't go back to schedule */ frame->gprs[9] = (unsigned long) frame; - + frame->pgm_svc_step=0; /* Nope we aren't single stepping an svc */ /* save fprs, if used in last task */ save_fp_regs(&p->tss.fp_regs); - p->tss.user_seg = __pa((unsigned long) p->mm->pgd) | _USER_SEG_TABLE_LEN; + p->tss.user_seg = __pa((unsigned long) p->mm->pgd) | _SEGMENT_TABLE; p->tss.fs = USER_DS; /* Don't copy debug registers */ memset(&p->tss.per_info,0,sizeof(p->tss.per_info)); diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index e5c1fb998b8b..0938e5c0c482 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -2,7 +2,7 @@ * arch/s390/kernel/ptrace.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), * * Based on PowerPC version @@ -226,7 +226,9 @@ static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigne static int read_long(struct task_struct * tsk, unsigned long addr, unsigned long * result) { - struct vm_area_struct * vma = find_extend_vma(tsk, addr); + struct vm_area_struct * vma; + addr=ADDR_BITS_REMOVE(addr); + vma= find_extend_vma(tsk, addr); if (!vma) return -EIO; @@ -268,7 +270,10 @@ static int read_long(struct task_struct * tsk, unsigned long addr, static int write_long(struct task_struct * tsk, unsigned long addr, unsigned long data) { - struct vm_area_struct * vma = find_extend_vma(tsk, addr); + struct vm_area_struct * vma; + + addr=ADDR_BITS_REMOVE(addr); + vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S new file mode 100644 index 000000000000..08c057ee377b --- /dev/null +++ b/arch/s390/kernel/reipl.S @@ -0,0 +1,72 @@ +/* + * arch/s390/kernel/reipl.S + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com) + */ + +#include + + .globl do_reipl +do_reipl: basr %r13,0 +.Lpg0: lpsw .Lnewpsw-.Lpg0(%r13) +.Lpg1: lctl %c6,%c6,.Lall-.Lpg0(%r13) + lr %r1,%r2 + mvc __LC_PGM_NEW_PSW(8,0),.Lpcnew-.Lpg0(%r13) + stsch .Lschib-.Lpg0(%r13) + oi .Lschib+5-.Lpg0(%r13),0x84 +.Lecs: xi .Lschib+27-.Lpg0(%r13),0x01 + msch .Lschib-.Lpg0(%r13) + ssch .Liplorb-.Lpg0(%r13) + jz .L001 + bas %r14,.Ldisab-.Lpg0(%r13) +.L001: mvc __LC_IO_NEW_PSW(8,0),.Lionew-.Lpg0(%r13) +.Ltpi: lpsw .Lwaitpsw-.Lpg0(%r13) +.Lcont: c %r1,__LC_SUBCHANNEL_ID(%r0) + jnz .Ltpi + clc __LC_IO_INT_PARM(4),.Liplorb-.Lpg0(%r13) + jnz .Ltpi + tsch .Liplirb-.Lpg0(%r13) + tm .Liplirb+9-.Lpg0(%r13),0xbf + jz .L002 + bas %r14,.Ldisab-.Lpg0(%r13) +.L002: tm .Liplirb+8-.Lpg0(%r13),0xf3 + jz .L003 + bas %r14,.Ldisab-.Lpg0(%r13) +.L003: spx .Lnull-.Lpg0(%r13) + st %r1,__LC_SUBCHANNEL_ID(%r0) + lpsw 0 + sigp 0,0,0(6) +.Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) + lpsw .Ldispsw-.Lpg0(%r13) + .align 8 +.Lall: .long 0xff000000; +.Lnull: .long 0x00000000 + .align 8 +.Lnewpsw: .long 0x00080000,0x80000000+.Lpg1 +.Lpcnew: .long 0x00080000,0x80000000+.Lecs +.Lionew: .long 0x00080000,0x80000000+.Lcont +.Lwaitpsw: .long 0x020a0000,0x00000000+.Ltpi +.Ldispsw: .long 0x000a0000,0x00000000 +.Liplccws: .long 0x02000000,0x60000018 + .long 0x08000008,0x20000001 +.Liplorb: .long 0x0049504c,0x0000ff80 + .long 0x00000000+.Liplccws +.Lschib: .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Liplirb: .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + + + diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c new file mode 100644 index 000000000000..6324520c8bd7 --- /dev/null +++ b/arch/s390/kernel/s390_ksyms.c @@ -0,0 +1,26 @@ +/* + * arch/s390/kernel/s390_ksyms.c + * + * S390 version + */ +#include +#include +#include +#include + +EXPORT_SYMBOL(get_dev_info_by_irq); +EXPORT_SYMBOL(get_dev_info_by_devno); +EXPORT_SYMBOL(get_irq_by_devno); +EXPORT_SYMBOL(get_devno_by_irq); +EXPORT_SYMBOL(get_irq_first); +EXPORT_SYMBOL(get_irq_next); +EXPORT_SYMBOL(halt_IO); +EXPORT_SYMBOL(resume_IO); +EXPORT_SYMBOL(do_IO); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(init_mm); +EXPORT_SYMBOL(_oi_bitmap); +EXPORT_SYMBOL(_ni_bitmap); +EXPORT_SYMBOL(_zb_findmap); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(ioinfo); diff --git a/arch/s390/kernel/s390io.c b/arch/s390/kernel/s390io.c index 4c93079e5293..8886ae8d7d0a 100644 --- a/arch/s390/kernel/s390io.c +++ b/arch/s390/kernel/s390io.c @@ -3,7 +3,8 @@ * S/390 common I/O routines * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, + IBM Corporation * Author(s): Ingo Adlung (adlung@de.ibm.com) */ @@ -27,9 +28,7 @@ #include #include #include - -#include "irq.h" -#include "s390io.h" +#include #undef CONFIG_DEBUG_IO @@ -37,16 +36,31 @@ struct irqaction init_IRQ_action; unsigned int highest_subchannel; -senseid_t senseid[NR_IRQS]; -schib_t schiblock[NR_IRQS]; -ioinfo_t ioinfo[NR_IRQS]; +ioinfo_t *ioinfo_head = NULL; +ioinfo_t *ioinfo_tail = NULL; +ioinfo_t *ioinfo[__MAX_SUBCHANNELS] = { + [0 ... (__MAX_SUBCHANNELS-1)] = INVALID_STORAGE_AREA +}; spinlock_t sync_isc; // synchronous irq processing lock psw_t io_sync_wait; // wait PSW for sync IO, prot. by sync_isc psw_t io_new_psw; // save I/O new PSW, prot. by sync_isc int cons_dev = -1; // identify console device int init_IRQ_complete = 0; +schib_t init_schib; + +/* + * Dummy controller type for unused interrupts + */ +int do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;} +int enable_none(unsigned int irq) { return(-ENODEV); } +int disable_none(unsigned int irq) { return(-ENODEV); } -extern struct hw_interrupt_type no_irq_type; +struct hw_interrupt_type no_irq_type = { + "none", + do_none, + enable_none, + disable_none +}; static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs); static int s390_setup_irq(unsigned int irq, struct irqaction * new); @@ -54,7 +68,7 @@ static void s390_process_subchannels( void); static void s390_device_recognition( void); static int s390_validate_subchannel( int irq); static int s390_SenseID( int irq, senseid_t *sid); -static int s390_process_IRQ( unsigned int irq, unsigned int intparm); +static int s390_process_IRQ( unsigned int irq ); extern int do_none(unsigned int irq, int cpu, struct pt_regs * regs); extern int enable_none(unsigned int irq); @@ -63,7 +77,13 @@ extern void tod_wait(unsigned long usecs); asmlinkage void do_IRQ( struct pt_regs regs, unsigned int irq, - unsigned int intparm ); + __u32 s390_intparm ); + +// +// fix me ! must be removed with 2.3.x and follow-up releases +// +static void * alloc_bootmem( unsigned long size); +static unsigned long memory_start = 0; void s390_displayhex(char *str,void *ptr,s32 cnt); @@ -73,6 +93,7 @@ void s390_displayhex(char *str,void *ptr,s32 cnt) u32 *currptr=(u32 *)ptr; printk("\n%s\n",str); + for(cnt1=0;cnt1= NR_IRQS) + if (irq >= __MAX_SUBCHANNELS) return -EINVAL; if ( !handler || !dev_id ) @@ -147,7 +168,7 @@ void s390_free_irq(unsigned int irq, void *dev_id) unsigned int count = 0; - if ( irq >= NR_IRQS ) + if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA ) { return; @@ -167,37 +188,42 @@ void s390_free_irq(unsigned int irq, void *dev_id) * disable the device and reset all IRQ info if * the IRQ is actually owned by the handler ... */ - if ( irq_desc[irq].action ) + if ( ioinfo[irq]->irq_desc.action ) { - if ( irq_desc[irq].action->dev_id == dev_id || - dev_id == REIPL_DEVID_MAGIC ) + if ( (dev_id == ioinfo[irq]->irq_desc.action->dev_id ) + || (dev_id == (devstat_t *)REIPL_DEVID_MAGIC) ) { - ioinfo[irq].ui.flags.unready = 1; /* start deregister */ + /* start deregister */ + ioinfo[irq]->ui.flags.unready = 1; do { - ret = irq_desc[irq].handler->disable(irq); + ret = ioinfo[irq]->irq_desc.handler->disable(irq); count++; if ( count == 3 ) { - panic( "free_irq() - device busy, retry count exceeded\n"); + printk( KERN_CRIT"free_irq(%04X) " + "- device %04X busy, retry " + "count exceeded\n", + irq, + ioinfo[irq]->devstat.devno); } /* endif */ } while ( ret == -EBUSY ); if ( init_IRQ_complete ) - kfree( irq_desc[irq].action ); + kfree( ioinfo[irq]->irq_desc.action ); - irq_desc[irq].action = NULL; - ioinfo[irq].ui.flags.ready = 0; + ioinfo[irq]->irq_desc.action = NULL; + ioinfo[irq]->ui.flags.ready = 0; - irq_desc[irq].handler->enable = &enable_none; - irq_desc[irq].handler->disable = &disable_none; + ioinfo[irq]->irq_desc.handler->enable = &enable_none; + ioinfo[irq]->irq_desc.handler->disable = &disable_none; - ioinfo[irq].ui.flags.unready = 0; /* deregister ended */ + ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */ s390irq_spin_unlock_irqrestore( irq, flags); } @@ -228,14 +254,17 @@ int disable_irq(unsigned int irq) unsigned long flags; int ret; + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + s390irq_spin_lock_irqsave(irq, flags); /* * At this point we may actually have a pending interrupt being active * on another CPU. So don't touch the IRQ_INPROGRESS bit.. */ - irq_desc[irq].status |= IRQ_DISABLED; - ret = irq_desc[irq].handler->disable(irq); + ioinfo[irq]->irq_desc.status |= IRQ_DISABLED; + ret = ioinfo[irq]->irq_desc.handler->disable(irq); s390irq_spin_unlock_irqrestore(irq, flags); synchronize_irq(); @@ -248,10 +277,13 @@ int enable_irq(unsigned int irq) unsigned long flags; int ret; + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + s390irq_spin_lock_irqsave(irq, flags); - irq_desc[irq].status = 0; - ret = irq_desc[irq].handler->enable(irq); + ioinfo[irq]->irq_desc.status = 0; + ret = ioinfo[irq]->irq_desc.handler->enable(irq); s390irq_spin_unlock_irqrestore(irq, flags); @@ -273,18 +305,21 @@ static int enable_subchannel( unsigned int irq) } /* endif */ + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + /* * If a previous disable request is pending we reset it. However, this * status implies that the device may (still) be not-operational. */ - if ( ioinfo[irq].ui.flags.d_disable ) + if ( ioinfo[irq]->ui.flags.d_disable ) { - ioinfo[irq].ui.flags.d_disable = 0; + ioinfo[irq]->ui.flags.d_disable = 0; ret = 0; } else { - ccode = stsch(irq, &(ioinfo[irq].schib) ); + ccode = stsch(irq, &(ioinfo[irq]->schib) ); if ( ccode ) { @@ -292,11 +327,11 @@ static int enable_subchannel( unsigned int irq) } else { - ioinfo[irq].schib.pmcw.ena = 1; + ioinfo[irq]->schib.pmcw.ena = 1; do { - ccode = msch( irq, &(ioinfo[irq].schib) ); + ccode = msch( irq, &(ioinfo[irq]->schib) ); switch (ccode) { case 0: @@ -305,30 +340,38 @@ static int enable_subchannel( unsigned int irq) case 1: /* - * very bad, requires interrupt alike processing, where - * "rbh" is a dummy parameter for interface compatibility - * only. Bottom-half handling cannot be required as - * this must be an unsolicited interrupt (!busy). + * very bad, requires interrupt alike + * processing, where "rbh" is a dummy + * parameter for interface compatibility + * only. Bottom-half handling cannot be + * required as this must be an + * unsolicited interrupt (!busy). */ - ioinfo[irq].ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.s_pend = 1; - s390_process_IRQ( irq, 0 ); + s390_process_IRQ( irq ); - ioinfo[irq].ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.s_pend = 0; - ret = -EIO; /* might be overwritten on ... */ - /* ... re-driving the msch() */ + ret = -EIO; /* might be overwritten */ + /* ... on re-driving */ + /* ... the msch() */ retry--; break; case 3: - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; ret = -ENODEV; break; default: - panic( "enable_subchannel() : ccode 2 on msch() received !\n"); + printk( KERN_CRIT"enable_subchannel(%04X) " + " : ccode 2 on msch() for device " + "%04X received !\n", + irq, + ioinfo[irq]->devstat.devno); + ret = -ENODEV; // never reached } @@ -355,7 +398,11 @@ static int disable_subchannel( unsigned int irq) { ret = -ENODEV; } - else if ( ioinfo[irq].ui.flags.busy ) + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + else if ( ioinfo[irq]->ui.flags.busy ) { /* * the disable function must not be called while there are @@ -370,22 +417,22 @@ static int disable_subchannel( unsigned int irq) * disabling when the next interrupt occurs - unless the * irq is re-requested prior to the interrupt to occur. */ - cc = stsch(irq, &(ioinfo[irq].schib) ); + cc = stsch(irq, &(ioinfo[irq]->schib) ); if ( cc == 3 ) { - ioinfo[irq].ui.flags.oper = 0; - ioinfo[irq].ui.flags.d_disable = 1; + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->ui.flags.d_disable = 1; ret = 0; } else // cc == 0 { - ioinfo[irq].schib.pmcw.ena = 0; + ioinfo[irq]->schib.pmcw.ena = 0; do { - cc = msch( irq, &(ioinfo[irq].schib) ); + cc = msch( irq, &(ioinfo[irq]->schib) ); switch (cc) { case 0 : @@ -394,19 +441,20 @@ static int disable_subchannel( unsigned int irq) case 1 : /* - * very bad, requires interrupt alike processing, where - * "rbh" is a dummy parameter for interface compatibility - * only. Bottom-half handling cannot be required as - * this must be an unsolicited interrupt (!busy). - */ - ioinfo[irq].ui.flags.s_pend = 1; - - s390_process_IRQ( irq, 0 ); - - ioinfo[irq].ui.flags.s_pend = 0; + * very bad, requires interrupt alike + * processing, where "rbh" is a dummy + * parm for interface compatibility + * only. Bottom-half handling cannot + * be required as this must be an + * unsolicited interrupt (!busy). + */ + ioinfo[irq]->ui.flags.s_pend = 1; + s390_process_IRQ( irq ); + ioinfo[irq]->ui.flags.s_pend = 0; - ret = -EBUSY; /* might be overwritten on ... */ - /* ... re-driving the msch() */ + ret = -EBUSY; /* might be overwritten */ + /* ... on re-driving the */ + /* ... msch() call */ retry--; break; @@ -414,22 +462,28 @@ static int disable_subchannel( unsigned int irq) /* * *** must not occur ! *** * *** *** - * *** indicates our internal interrupt accounting is out *** + * *** indicates our internal *** + * *** interrupt accounting is out *** * *** of sync ===> panic() *** */ - panic( "disable_subchannel() : unexpected busy condition !\n"); + printk( KERN_CRIT"disable_subchannel(%04X) " + "- unexpected busy condition for " + "device %04X received !\n", + irq, + ioinfo[irq]->devstat.devno); ret = -ENODEV; // never reached break; case 3 : /* - * should hardly occur but not impossible ... + * should hardly occur ?! */ - ioinfo[irq].ui.flags.oper = 0; - ioinfo[irq].ui.flags.d_disable = 1; + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->ui.flags.d_disable = 1; - ret = 0; /* if the device has gone we don't ... */ - /* ... need to disable it anymore ! */ + ret = 0; /* if the device has gone we */ + /* ... don't need to disable */ + /* ... it anymore ! */ break; default : @@ -454,22 +508,27 @@ int s390_setup_irq(unsigned int irq, struct irqaction * new) unsigned long flags; int rc = 0; + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + /* * The following block of code has to be executed atomically */ s390irq_spin_lock_irqsave( irq, flags); - if ( irq_desc[irq].action == NULL ) + if ( ioinfo[irq]->irq_desc.action == NULL ) { - irq_desc[irq].action = new; - irq_desc[irq].status = 0; - irq_desc[irq].handler->enable = &enable_subchannel; - irq_desc[irq].handler->disable = &disable_subchannel; - irq_desc[irq].handler->handle = &handle_IRQ_event; + ioinfo[irq]->irq_desc.action = new; + ioinfo[irq]->irq_desc.status = 0; + ioinfo[irq]->irq_desc.handler->enable = &enable_subchannel; + ioinfo[irq]->irq_desc.handler->disable = &disable_subchannel; + ioinfo[irq]->irq_desc.handler->handle = &handle_IRQ_event; - ioinfo[irq].ui.flags.ready = 1; + ioinfo[irq]->ui.flags.ready = 1; - irq_desc[irq].handler->enable(irq); + ioinfo[irq]->irq_desc.handler->enable(irq); } else { @@ -486,19 +545,26 @@ int s390_setup_irq(unsigned int irq, struct irqaction * new) return( rc); } +/* + * we need boot memory during init processing but don't have + * kmalloc() available yet. Linux 2.3 introduced alloc_bootmem() + * we kind of emulate for the time being. + */ +static void * alloc_bootmem( unsigned long size) +{ + void * ret = (void *)memory_start; + + memory_start += size; -void s390_init_IRQ( void ) + return( ret ); +} + +unsigned long s390_init_IRQ( unsigned long memstart) { unsigned long flags; /* PSW flags */ long cr6 __attribute__ ((aligned (8))); - int irq; /* counter for I/O subchannels */ - - for (irq=0; irqorb), '\0', sizeof( orb_t) ); /* * setup ORB */ - ioinfo[irq].orb.intparm = intparm; - ioinfo[irq].orb.fmt = 1; + ioinfo[irq]->orb.fmt = 1; - ioinfo[irq].orb.pfch = !(flag & DOIO_DENY_PREFETCH); - ioinfo[irq].orb.spnd = (flag & DOIO_ALLOW_SUSPEND); - ioinfo[irq].orb.ssic = ( (flag & DOIO_ALLOW_SUSPEND ) + ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH); + ioinfo[irq]->orb.spnd = (flag & DOIO_ALLOW_SUSPEND); + ioinfo[irq]->orb.ssic = ( (flag & DOIO_ALLOW_SUSPEND ) && (flag & DOIO_SUPPRESS_INTER) ); if ( flag & DOIO_VALID_LPM ) { - ioinfo[irq].orb.lpm = lpm; + ioinfo[irq]->orb.lpm = lpm; } else { - ioinfo[irq].orb.lpm = ioinfo[irq].schib.pmcw.pam; + ioinfo[irq]->orb.lpm = ioinfo[irq]->schib.pmcw.pam; } /* endif */ - ioinfo[irq].orb.cpa = (ccw1_t *)virt_to_phys( cpa); + ioinfo[irq]->orb.cpa = (ccw1_t *)virt_to_phys( cpa); /* * If sync processing was requested we lock the sync ISC, modify the @@ -605,7 +672,7 @@ int s390_start_IO( int irq, /* IRQ */ // // check whether we run recursively (sense processing) // - if ( !ioinfo[irq].ui.flags.syncio ) + if ( !ioinfo[irq]->ui.flags.syncio ) { spin_lock_irqsave( &sync_isc, psw_flags); @@ -614,14 +681,12 @@ int s390_start_IO( int irq, /* IRQ */ if ( ret ) { spin_unlock_irqrestore( &sync_isc, psw_flags); - - // sigh, there should be a single exit point only ... return( ret); } else { - sync_isc_locked = 1; // local setting - ioinfo[irq].ui.flags.syncio = 1; // global setting + sync_isc_locked = 1; // local + ioinfo[irq]->ui.flags.syncio = 1; // global } /* endif */ @@ -632,37 +697,37 @@ int s390_start_IO( int irq, /* IRQ */ /* * Issue "Start subchannel" and process condition code */ - ccode = ssch( irq, &(ioinfo[irq].orb) ); + ccode = ssch( irq, &(ioinfo[irq]->orb) ); switch ( ccode ) { case 0: - if ( !ioinfo[irq].ui.flags.w4sense ) + if ( !ioinfo[irq]->ui.flags.w4sense ) { /* - * initialize the device driver specific devstat irb area + * init the device driver specific devstat irb area * - * Note : don´t clear saved irb information in case of sense ! + * Note : don´t clear saved irb info in case of sense ! */ - memset( &((devstat_t *) irq_desc[irq].action->dev_id)->ii.irb, + memset( &((devstat_t *)ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, '\0', sizeof( irb_t) ); } /* endif */ /* * initialize device status information */ - ioinfo[irq].ui.flags.busy = 1; - ioinfo[irq].ui.flags.doio = 1; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; - ioinfo[irq].devstat.intparm = intparm; - ioinfo[irq].devstat.cstat = 0; - ioinfo[irq].devstat.dstat = 0; - ioinfo[irq].devstat.lpum = 0; - ioinfo[irq].devstat.flag = DEVSTAT_START_FUNCTION; - ioinfo[irq].devstat.scnt = 0; + ioinfo[irq]->u_intparm = user_intparm; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.lpum = 0; + ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION; + ioinfo[irq]->devstat.scnt = 0; - ioinfo[irq].ui.flags.fast = 0; - ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; /* * Check for either early (FAST) notification requests @@ -671,21 +736,21 @@ int s390_start_IO( int irq, /* IRQ */ */ if ( flag & DOIO_RETURN_CHAN_END ) { - ioinfo[irq].ui.flags.fast = 1; + ioinfo[irq]->ui.flags.fast = 1; } else if ( flag & DOIO_REPORT_ALL ) { - ioinfo[irq].ui.flags.repall = 1; + ioinfo[irq]->ui.flags.repall = 1; } /* endif */ if ( flag & DOIO_VALID_LPM ) { - ioinfo[irq].lpm = lpm; /* specific path */ + ioinfo[irq]->lpm = lpm; /* specific path */ } else { - ioinfo[irq].lpm = 0xff; /* any path */ + ioinfo[irq]->lpm = 0xff; /* any path */ } /* endif */ @@ -700,7 +765,7 @@ int s390_start_IO( int irq, /* IRQ */ if ( flag & DOIO_WAIT_FOR_INTERRUPT ) { int io_sub; - int io_parm; + __u32 io_parm; psw_t io_new_psw; int ccode; @@ -708,11 +773,12 @@ int s390_start_IO( int irq, /* IRQ */ struct _lowcore *lc = NULL; /* - * We shouldn't perform a TPI loop, waiting for an interrupt - * to occur, but should load a WAIT PSW instead. Otherwise - * we may keep the channel subsystem busy, not able to present - * the interrupt. When our sync. interrupt arrived we reset - * the I/O old PSW to its original value. + * We shouldn't perform a TPI loop, waiting for an + * interrupt to occur, but should load a WAIT PSW + * instead. Otherwise we may keep the channel subsystem + * busy, not able to present the interrupt. When our + * sync. interrupt arrived we reset the I/O old PSW to + * its original value. */ memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); @@ -746,27 +812,28 @@ int s390_start_IO( int irq, /* IRQ */ break; } /* endswitch */ - io_sync_wait.addr = (unsigned long) &&io_wakeup | 0x80000000L; + io_sync_wait.addr = (unsigned long) &&io_wakeup + | 0x80000000L; /* * Martin didn't like modifying the new PSW, now we take * a fast exit in do_IRQ() instead */ - *(int *)__LC_SYNC_IO_WORD = 1; + *(__u32 *)__LC_SYNC_IO_WORD = 1; do { asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); io_wakeup: - io_parm = *(int *)__LC_IO_INT_PARM; - io_sub = (int)*(short *)__LC_SUBCHANNEL_NR; + io_parm = *(__u32 *)__LC_IO_INT_PARM; + io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; - ready = s390_process_IRQ( io_sub, io_parm); + ready = s390_process_IRQ( io_sub ); } while ( !((io_sub == irq) && (ready == 1)) ); - *(int *)__LC_SYNC_IO_WORD = 0; + *(__u32 *)__LC_SYNC_IO_WORD = 0; } /* endif */ @@ -774,35 +841,35 @@ io_wakeup: case 1 : /* status pending */ - ioinfo[irq].devstat.flag |= DEVSTAT_STATUS_PENDING; + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; /* * initialize the device driver specific devstat irb area */ - memset( &((devstat_t *) irq_desc[irq].action->dev_id)->ii.irb, + memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, '\0', sizeof( irb_t) ); /* * Let the common interrupt handler process the pending status. * However, we must avoid calling the user action handler, as - * it won't be prepared to handle a pending status during do_IO() - * processing inline. This also implies that process_IRQ must - * terminate synchronously - especially if device sensing is - * required. - */ - ioinfo[irq].ui.flags.s_pend = 1; - ioinfo[irq].ui.flags.busy = 1; - ioinfo[irq].ui.flags.doio = 1; + * it won't be prepared to handle a pending status during + * do_IO() processing inline. This also implies that process_IRQ + * must terminate synchronously - especially if device sensing + * is required. + */ + ioinfo[irq]->ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; - s390_process_IRQ( irq, intparm ); + s390_process_IRQ( irq ); - ioinfo[irq].ui.flags.s_pend = 0; - ioinfo[irq].ui.flags.busy = 0; - ioinfo[irq].ui.flags.doio = 0; - ioinfo[irq].ui.flags.repall = 0; - ioinfo[irq].ui.flags.w4final = 0; + ioinfo[irq]->ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; - ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; /* * In multipath mode a condition code 3 implies the last path @@ -811,47 +878,48 @@ io_wakeup: * results in return code EIO as well as 3 with another path * than the one used (i.e. path available mask is non-zero). */ - if ( ioinfo[irq].devstat.ii.irb.scsw.cc == 3 ) + if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) { ret = -ENODEV; - ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 0; #if CONFIG_DEBUG_IO { char buffer[80]; - stsch(irq, &(ioinfo[irq].schib) ); + stsch(irq, &(ioinfo[irq]->schib) ); sprintf( buffer, "s390_start_IO(%04X) - irb for " "device %04X, after status pending\n", irq, - ioinfo[irq].devstat.devno ); + ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, - &(ioinfo[irq].devstat.ii.irb) , + &(ioinfo[irq]->devstat.ii.irb) , sizeof(irb_t)); sprintf( buffer, "s390_start_IO(%04X) - schib for " "device %04X, after status pending\n", irq, - ioinfo[irq].devstat.devno ); + ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, - &(ioinfo[irq].schib) , + &(ioinfo[irq]->schib) , sizeof(schib_t)); - if (ioinfo[irq].devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) + if (ioinfo[irq]->devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) { - sprintf( buffer, "s390_start_IO(%04X) - sense data for " + sprintf( buffer, "s390_start_IO(%04X) - sense " + "data for " "device %04X, after status pending\n", irq, - ioinfo[irq].devstat.devno ); + ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, - ((devstat_t *)(irq_desc[irq].action->dev_id))->ii.sense.data, - ((devstat_t *)(irq_desc[irq].action->dev_id))->rescnt); + ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->ii.sense.data, + ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->rescnt); } } @@ -860,8 +928,8 @@ io_wakeup: else { ret = -EIO; - ioinfo[irq].devstat.flag &= ~DEVSTAT_NOT_OPER; - ioinfo[irq].ui.flags.oper = 1; + ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 1; } /* endif */ @@ -875,28 +943,27 @@ io_wakeup: default: /* device not operational */ ret = -ENODEV; - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; - ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq].devstat.intparm = intparm; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - memcpy( irq_desc[irq].action->dev_id, - &(ioinfo[irq].devstat), + memcpy( ioinfo[irq]->irq_desc.action->dev_id, + &(ioinfo[irq]->devstat), sizeof( devstat_t) ); #if CONFIG_DEBUG_IO { char buffer[80]; - stsch(irq, &(ioinfo[irq].schib) ); + stsch(irq, &(ioinfo[irq]->schib) ); sprintf( buffer, "s390_start_IO(%04X) - schib for " "device %04X, after 'not oper' status\n", irq, - ioinfo[irq].devstat.devno ); + ioinfo[irq]->devstat.devno ); s390_displayhex( buffer, - &(ioinfo[irq].schib), + &(ioinfo[irq]->schib), sizeof(schib_t)); } #endif @@ -912,7 +979,7 @@ io_wakeup: spin_unlock_irqrestore( &sync_isc, psw_flags); sync_isc_locked = 0; // local setting - ioinfo[irq].ui.flags.syncio = 0; // global setting + ioinfo[irq]->ui.flags.syncio = 0; // global setting } /* endif */ @@ -921,8 +988,8 @@ io_wakeup: int do_IO( int irq, /* IRQ */ ccw1_t *cpa, /* channel program address */ - unsigned long intparm, /* interruption parameter */ - unsigned char lpm, /* logical path mask */ + unsigned long user_intparm, /* interruption parameter */ + __u8 lpm, /* logical path mask */ unsigned long flag) /* flags : see above */ { int ret = 0; @@ -933,8 +1000,13 @@ int do_IO( int irq, /* IRQ */ } /* endif */ + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + /* handler registered ? */ - if ( !ioinfo[irq].ui.flags.ready ) + if ( !ioinfo[irq]->ui.flags.ready ) { return( -ENODEV ); @@ -944,27 +1016,28 @@ int do_IO( int irq, /* IRQ */ * Note: We ignore the device operational status - if not operational, * the SSCH will lead to an -ENODEV condition ... */ - if ( !ioinfo[irq].ui.flags.busy ) /* last I/O completed ? */ + if ( !ioinfo[irq]->ui.flags.busy ) /* last I/O completed ? */ { - ret = s390_start_IO( irq, cpa, intparm, lpm, flag); + ret = s390_start_IO( irq, cpa, user_intparm, lpm, flag); } - else if ( ioinfo[irq].ui.flags.fast ) + else if ( ioinfo[irq]->ui.flags.fast ) { /* - * If primary status was received and ending status is missing, the - * device driver won't be notified on the ending status if early - * (fast) interrupt notification was requested. Therefore we have - * to queue the next incoming request. If halt_IO() is issued while - * there is a request queued, a HSCH needs to be issued and the queued - * request must be deleted but its intparm must be returned (see - * halt_IO() processing) + * If primary status was received and ending status is missing, + * the device driver won't be notified on the ending status + * if early (fast) interrupt notification was requested. + * Therefore we have to queue the next incoming request. If + * halt_IO() is issued while there is a request queued, a HSCH + * needs to be issued and the queued request must be deleted + * but its intparm must be returned (see halt_IO() processing) */ - if ( ioinfo[irq].ui.flags.w4final && !ioinfo[irq].ui.flags.doio_q ) + if ( ioinfo[irq]->ui.flags.w4final + && !ioinfo[irq]->ui.flags.doio_q ) { - ioinfo[irq].qflag = flag; - ioinfo[irq].qcpa = cpa; - ioinfo[irq].qintparm = intparm; - ioinfo[irq].qlpm = lpm; + ioinfo[irq]->qflag = flag; + ioinfo[irq]->qcpa = cpa; + ioinfo[irq]->qintparm = user_intparm; + ioinfo[irq]->qlpm = lpm; } else { @@ -995,10 +1068,15 @@ int resume_IO( int irq) } /* endif */ + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + /* * We allow for 'resume' requests only for active I/O operations */ - if ( ioinfo[irq].ui.flags.busy ) + if ( ioinfo[irq]->ui.flags.busy ) { int ccode; @@ -1009,8 +1087,7 @@ int resume_IO( int irq) break; case 1 : - s390_process_IRQ( irq, - ioinfo[irq].devstat.intparm ); + s390_process_IRQ( irq ); ret = -EBUSY; break; @@ -1023,8 +1100,8 @@ int resume_IO( int irq) * useless to wait for request completion * as device is no longer operational ! */ - ioinfo[irq].ui.flags.oper = 0; - ioinfo[irq].ui.flags.busy = 0; + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->ui.flags.busy = 0; ret = -ENODEV; break; @@ -1047,7 +1124,7 @@ int resume_IO( int irq) * interrupt with the halt_IO() request. */ int halt_IO( int irq, - int intparm, + unsigned long user_intparm, unsigned int flag) /* possible DOIO_WAIT_FOR_INTERRUPT */ { int ret; @@ -1060,10 +1137,16 @@ int halt_IO( int irq, { ret = -ENODEV; } + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + /* * we only allow for halt_IO if the device has an I/O handler associated */ - else if ( !ioinfo[irq].ui.flags.ready ) + else if ( !ioinfo[irq]->ui.flags.ready ) { ret = -ENODEV; } @@ -1071,30 +1154,31 @@ int halt_IO( int irq, * we ignore the halt_io() request if ending_status was received but * a SENSE operation is waiting for completion. */ - else if ( ioinfo[irq].ui.flags.w4sense ) + else if ( ioinfo[irq]->ui.flags.w4sense ) { ret = 0; } /* * We don't allow for halt_io with a sync do_IO() requests pending. */ - else if ( ioinfo[irq].ui.flags.syncio ) + else if ( ioinfo[irq]->ui.flags.syncio ) { ret = -EBUSY; } else { /* - * If sync processing was requested we lock the sync ISC, modify the - * device to present interrupts for this ISC only and switch the - * CPU to handle this ISC + the console ISC exclusively. + * If sync processing was requested we lock the sync ISC, + * modify the device to present interrupts for this ISC only + * and switch the CPU to handle this ISC + the console ISC + * exclusively. */ if ( flag & DOIO_WAIT_FOR_INTERRUPT ) { // // check whether we run recursively (sense processing) // - if ( !ioinfo[irq].ui.flags.syncio ) + if ( !ioinfo[irq]->ui.flags.syncio ) { spin_lock_irqsave( &sync_isc, psw_flags); @@ -1102,15 +1186,14 @@ int halt_IO( int irq, if ( ret ) { - spin_unlock_irqrestore( &sync_isc, psw_flags); - - // sigh, there should be a single exit point only ... + spin_unlock_irqrestore( &sync_isc, + psw_flags); return( ret); } else { - sync_isc_locked = 1; // local setting - ioinfo[irq].ui.flags.syncio = 1; // global setting + sync_isc_locked = 1; // local + ioinfo[irq]->ui.flags.syncio = 1; // global } /* endif */ @@ -1126,22 +1209,22 @@ int halt_IO( int irq, switch ( ccode ) { case 0: - ioinfo[irq].ui.flags.haltio = 1; + ioinfo[irq]->ui.flags.haltio = 1; - if ( !ioinfo[irq].ui.flags.doio ) + if ( !ioinfo[irq]->ui.flags.doio ) { - ioinfo[irq].ui.flags.busy = 1; - ioinfo[irq].devstat.intparm = intparm; - ioinfo[irq].devstat.cstat = 0; - ioinfo[irq].devstat.dstat = 0; - ioinfo[irq].devstat.lpum = 0; - ioinfo[irq].devstat.flag = DEVSTAT_HALT_FUNCTION; - ioinfo[irq].devstat.scnt = 0; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->u_intparm = user_intparm; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.lpum = 0; + ioinfo[irq]->devstat.flag = DEVSTAT_HALT_FUNCTION; + ioinfo[irq]->devstat.scnt = 0; } else { - ioinfo[irq].devstat.flag |= DEVSTAT_HALT_FUNCTION; + ioinfo[irq]->devstat.flag |= DEVSTAT_HALT_FUNCTION; } /* endif */ @@ -1156,7 +1239,7 @@ int halt_IO( int irq, if ( flag & DOIO_WAIT_FOR_INTERRUPT ) { int io_sub; - int io_parm; + __u32 io_parm; psw_t io_new_psw; int ccode; @@ -1164,13 +1247,17 @@ int halt_IO( int irq, struct _lowcore *lc = NULL; /* - * We shouldn't perform a TPI loop, waiting for an interrupt - * to occur, but should load a WAIT PSW instead. Otherwise - * we may keep the channel subsystem busy, not able to present - * the interrupt. When our sync. interrupt arrived we reset - * the I/O old PSW to its original value. - */ - memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); + * We shouldn't perform a TPI loop, waiting for + * an interrupt to occur, but should load a + * WAIT PSW instead. Otherwise we may keep the + * channel subsystem busy, not able to present + * the interrupt. When our sync. interrupt + * arrived we reset the I/O old PSW to its + * original value. + */ + memcpy( &io_new_psw, + &lc->io_new_psw, + sizeof(psw_t)); ccode = iac(); @@ -1202,27 +1289,28 @@ int halt_IO( int irq, break; } /* endswitch */ - io_sync_wait.addr = (unsigned long) &&hio_wakeup | 0x80000000L; + io_sync_wait.addr = (unsigned long)&&hio_wakeup + | 0x80000000L; /* * Martin didn't like modifying the new PSW, now we take * a fast exit in do_IRQ() instead */ - *(int *)__LC_SYNC_IO_WORD = 1; + *(__u32 *)__LC_SYNC_IO_WORD = 1; do { asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); hio_wakeup: - io_parm = *(int *)__LC_IO_INT_PARM; - io_sub = (int)*(__u16 *)__LC_SUBCHANNEL_NR; + io_parm = *(__u32 *)__LC_IO_INT_PARM; + io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; - ready = s390_process_IRQ( io_sub, io_parm); + ready = s390_process_IRQ( io_sub ); } while ( !((io_sub == irq) && (ready == 1)) ); - *(int *)__LC_SYNC_IO_WORD = 0; + *(__u32 *)__LC_SYNC_IO_WORD = 0; } /* endif */ @@ -1231,54 +1319,55 @@ hio_wakeup: case 1 : /* status pending */ - ioinfo[irq].devstat.flag |= DEVSTAT_STATUS_PENDING; + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; /* * initialize the device driver specific devstat irb area */ - memset( &((devstat_t *) irq_desc[irq].action->dev_id)->ii.irb, + memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, '\0', sizeof( irb_t) ); /* - * Let the common interrupt handler process the pending status. - * However, we must avoid calling the user action handler, as - * it won't be prepared to handle a pending status during do_IO() - * processing inline. This also implies that s390_process_IRQ must - * terminate synchronously - especially if device sensing is - * required. - */ - ioinfo[irq].ui.flags.s_pend = 1; - ioinfo[irq].ui.flags.busy = 1; - ioinfo[irq].ui.flags.doio = 1; - - s390_process_IRQ( irq, intparm ); - - ioinfo[irq].ui.flags.s_pend = 0; - ioinfo[irq].ui.flags.busy = 0; - ioinfo[irq].ui.flags.doio = 0; - ioinfo[irq].ui.flags.repall = 0; - ioinfo[irq].ui.flags.w4final = 0; - - ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + * Let the common interrupt handler process the pending + * status. However, we must avoid calling the user + * action handler, as it won't be prepared to handle + * a pending status during do_IO() processing inline. + * This also implies that s390_process_IRQ must + * terminate synchronously - especially if device + * sensing is required. + */ + ioinfo[irq]->ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; + + s390_process_IRQ( irq ); + + ioinfo[irq]->ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; /* - * In multipath mode a condition code 3 implies the last path - * has gone, except we have previously restricted the I/O to - * a particular path. A condition code 1 (0 won't occur) - * results in return code EIO as well as 3 with another path - * than the one used (i.e. path available mask is non-zero). + * In multipath mode a condition code 3 implies the last + * path has gone, except we have previously restricted + * the I/O to a particular path. A condition code 1 + * (0 won't occur) results in return code EIO as well + * as 3 with another path than the one used (i.e. path available mask is non-zero). */ - if ( ioinfo[irq].devstat.ii.irb.scsw.cc == 3 ) + if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) { ret = -ENODEV; - ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 0; } else { ret = -EIO; - ioinfo[irq].devstat.flag &= ~DEVSTAT_NOT_OPER; - ioinfo[irq].ui.flags.oper = 1; + ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 1; } /* endif */ @@ -1300,7 +1389,7 @@ hio_wakeup: && ( sync_isc_locked ) ) { sync_isc_locked = 0; // local setting - ioinfo[irq].ui.flags.syncio = 0; // global setting + ioinfo[irq]->ui.flags.syncio = 0; // global setting disable_cpu_sync_isc( irq ); @@ -1324,7 +1413,7 @@ hio_wakeup: */ asmlinkage void do_IRQ( struct pt_regs regs, unsigned int irq, - unsigned int intparm ) + __u32 s390_intparm ) { #ifdef CONFIG_FAST_IRQ int ccode; @@ -1332,7 +1421,18 @@ asmlinkage void do_IRQ( struct pt_regs regs, int new_irq; #endif int use_irq = irq; - long use_intparm = intparm; +// __u32 use_intparm = s390_intparm; + + // + // fix me !!! + // + // We need to schedule device recognition, the interrupt stays + // pending. We need to dynamically allocate an ioinfo structure. + // + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return; + } /* * take fast exit if CPU is in sync. I/O state @@ -1341,7 +1441,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, * interrupts prior to return as this was the initial * entry condition to synchronous I/O. */ - if ( *(int *)__LC_SYNC_IO_WORD ) + if ( *(__u32 *)__LC_SYNC_IO_WORD ) { regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT); @@ -1355,7 +1455,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, do { #endif /* CONFIG_FAST_IRQ */ - s390_process_IRQ( use_irq, use_intparm); + s390_process_IRQ( use_irq ); #ifdef CONFIG_FAST_IRQ @@ -1368,7 +1468,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, break; // no, leave ... new_irq = tpi_info.irq; - use_intparm = tpi_info.intparm; +// use_intparm = tpi_info.intparm; /* * if the interrupt is for a different irq we @@ -1403,8 +1503,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, * Returns: 0 - no ending status received, no further action taken * 1 - interrupt handler was called with ending status */ -int s390_process_IRQ( unsigned int irq, - unsigned int intparm) +int s390_process_IRQ( unsigned int irq ) { int ccode; /* condition code from tsch() operation */ int irb_cc; /* condition code from irb */ @@ -1418,11 +1517,13 @@ int s390_process_IRQ( unsigned int irq, int issense = 0; int ending_status = 0; int allow4handler = 1; + int chnchk = 0; +#if 0 int cpu = smp_processor_id(); kstat.irqs[cpu][irq]++; - - action = irq_desc[irq].action; +#endif + action = ioinfo[irq]->irq_desc.action; /* * It might be possible that a device was not-oper. at the time @@ -1432,9 +1533,13 @@ int s390_process_IRQ( unsigned int irq, */ if ( action == NULL ) { - if ( !ioinfo[irq].ui.flags.d_disable ) + if ( !ioinfo[irq]->ui.flags.d_disable ) { - panic( "do_IRQ() : error, no irq_action available !\n"); + printk( KERN_CRIT"s390_process_IRQ(%04X) " + "- no interrupt handler registered" + "for device %04X !\n", + irq, + ioinfo[irq]->devstat.devno); } /* endif */ @@ -1458,40 +1563,40 @@ int s390_process_IRQ( unsigned int irq, * issued for an idle device, the intparm must not * be taken from lowcore, but from the devstat area. */ - ccode = tsch( irq, &(ioinfo[irq].devstat.ii.irb) ); + ccode = tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); // // We must only accumulate the status if initiated by do_IO() or halt_IO() // - if ( ioinfo[irq].ui.flags.busy ) + if ( ioinfo[irq]->ui.flags.busy ) { - ioinfo[irq].devstat.dstat |= ioinfo[irq].devstat.ii.irb.scsw.dstat; - ioinfo[irq].devstat.cstat |= ioinfo[irq].devstat.ii.irb.scsw.cstat; + ioinfo[irq]->devstat.dstat |= ioinfo[irq]->devstat.ii.irb.scsw.dstat; + ioinfo[irq]->devstat.cstat |= ioinfo[irq]->devstat.ii.irb.scsw.cstat; } else { - ioinfo[irq].devstat.dstat = ioinfo[irq].devstat.ii.irb.scsw.dstat; - ioinfo[irq].devstat.cstat = ioinfo[irq].devstat.ii.irb.scsw.cstat; + ioinfo[irq]->devstat.dstat = ioinfo[irq]->devstat.ii.irb.scsw.dstat; + ioinfo[irq]->devstat.cstat = ioinfo[irq]->devstat.ii.irb.scsw.cstat; - ioinfo[irq].devstat.flag = 0; // reset status flags + ioinfo[irq]->devstat.flag = 0; // reset status flags } /* endif */ - ioinfo[irq].devstat.lpum = ioinfo[irq].devstat.ii.irb.esw.esw1.lpum; + ioinfo[irq]->devstat.lpum = ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum; - if ( ioinfo[irq].ui.flags.doio) + if ( ioinfo[irq]->ui.flags.busy) { - ioinfo[irq].devstat.intparm = intparm; + ioinfo[irq]->devstat.intparm = ioinfo[irq]->u_intparm; } /* endif */ /* * reset device-busy bit if no longer set in irb */ - if ( (ioinfo[irq].devstat.dstat & DEV_STAT_BUSY ) - && ((ioinfo[irq].devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) + if ( (ioinfo[irq]->devstat.dstat & DEV_STAT_BUSY ) + && ((ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) { - ioinfo[irq].devstat.dstat &= ~DEV_STAT_BUSY; + ioinfo[irq]->devstat.dstat &= ~DEV_STAT_BUSY; } /* endif */ @@ -1499,31 +1604,31 @@ int s390_process_IRQ( unsigned int irq, * Save residual count and CCW information in case primary and * secondary status are presented with different interrupts. */ - if ( ioinfo[irq].devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS ) + if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS ) { - ioinfo[irq].devstat.rescnt = ioinfo[irq].devstat.ii.irb.scsw.count; + ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count; #if CONFIG_DEBUG_IO if ( irq != cons_dev ) printk( "s390_process_IRQ( %04X ) : " "residual count from irb after tsch() %d\n", - irq, ioinfo[irq].devstat.rescnt ); + irq, ioinfo[irq]->devstat.rescnt ); #endif } /* endif */ - if ( ioinfo[irq].devstat.ii.irb.scsw.cpa != 0 ) + if ( ioinfo[irq]->devstat.ii.irb.scsw.cpa != 0 ) { - ioinfo[irq].devstat.cpa = ioinfo[irq].devstat.ii.irb.scsw.cpa; + ioinfo[irq]->devstat.cpa = ioinfo[irq]->devstat.ii.irb.scsw.cpa; } /* endif */ - irb_cc = ioinfo[irq].devstat.ii.irb.scsw.cc; + irb_cc = ioinfo[irq]->devstat.ii.irb.scsw.cc; // // check for any kind of channel or interface control check but don't // issue the message for the console device // - if ( (ioinfo[irq].devstat.ii.irb.scsw.cstat + if ( (ioinfo[irq]->devstat.ii.irb.scsw.cstat & ( SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK ) ) @@ -1531,22 +1636,24 @@ int s390_process_IRQ( unsigned int irq, { printk( "Channel-Check or Interface-Control-Check " "received\n" - " ... device %X on subchannel %X, dev_stat " - ": %X sch_stat : %X\n", - ioinfo[irq].devstat.devno, + " ... device %04X on subchannel %04X, dev_stat " + ": %02X sch_stat : %02X\n", + ioinfo[irq]->devstat.devno, irq, - ioinfo[irq].devstat.dstat, - ioinfo[irq].devstat.cstat); + ioinfo[irq]->devstat.dstat, + ioinfo[irq]->devstat.cstat); + + chnchk = 1; } /* endif */ - issense = ioinfo[irq].devstat.ii.irb.esw.esw0.erw.cons; + issense = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.cons; if ( issense ) { - ioinfo[irq].devstat.scnt = - ioinfo[irq].devstat.ii.irb.esw.esw0.erw.scnt; - ioinfo[irq].devstat.flag |= + ioinfo[irq]->devstat.scnt = + ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.scnt; + ioinfo[irq]->devstat.flag |= DEVSTAT_FLAG_SENSE_AVAIL; sdevstat = sizeof( devstat_t); @@ -1555,7 +1662,7 @@ int s390_process_IRQ( unsigned int irq, if ( irq != cons_dev ) printk( "s390_process_IRQ( %04X ) : " "concurrent sense bytes avail %d\n", - irq, ioinfo[irq].devstat.scnt ); + irq, ioinfo[irq]->devstat.scnt ); #endif } else @@ -1568,16 +1675,29 @@ int s390_process_IRQ( unsigned int irq, switch ( irb_cc ) { case 1: /* status pending */ - ioinfo[irq].devstat.flag |= DEVSTAT_STATUS_PENDING; + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; case 0: /* normal i/o interruption */ - fctl = ioinfo[irq].devstat.ii.irb.scsw.fctl; - stctl = ioinfo[irq].devstat.ii.irb.scsw.stctl; - actl = ioinfo[irq].devstat.ii.irb.scsw.actl; + fctl = ioinfo[irq]->devstat.ii.irb.scsw.fctl; + stctl = ioinfo[irq]->devstat.ii.irb.scsw.stctl; + actl = ioinfo[irq]->devstat.ii.irb.scsw.actl; + + if ( chnchk && (ioinfo[irq]->senseid.cu_type == 0x3088)) + { + char buffer[80]; + + sprintf( buffer, "s390_process_IRQ(%04X) - irb for " + "device %04X after channel check\n", + irq, + ioinfo[irq]->devstat.devno ); + s390_displayhex( buffer, + &(ioinfo[irq]->devstat.ii.irb) , + sizeof(irb_t)); + } /* endif */ - ioinfo[irq].stctl |= stctl; + ioinfo[irq]->stctl |= stctl; ending_status = ( stctl & SCSW_STCTL_SEC_STATUS ) || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) ) @@ -1597,25 +1717,25 @@ int s390_process_IRQ( unsigned int irq, #if CONFIG_DEBUG_IO if ( ( irq != cons_dev ) && !( stctl & SCSW_STCTL_ALERT_STATUS ) - && ( ioinfo[irq].ui.flags.busy == 0 ) ) + && ( ioinfo[irq]->ui.flags.busy == 0 ) ) { char buffer[80]; printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n" " ... device status : %02X subchannel status : %02X\n", - ioinfo[irq].devstat.devno, + ioinfo[irq]->devstat.devno, irq, - ioinfo[irq].devstat.dstat, - ioinfo[irq].devstat.cstat); + ioinfo[irq]->devstat.dstat, + ioinfo[irq]->devstat.cstat); sprintf( buffer, "s390_process_IRQ(%04X) - irb for " "device %04X, ending_status %d\n", irq, - ioinfo[irq].devstat.devno, + ioinfo[irq]->devstat.devno, ending_status); s390_displayhex( buffer, - &(ioinfo[irq].devstat.ii.irb) , + &(ioinfo[irq]->devstat.ii.irb) , sizeof(irb_t)); } /* endif */ @@ -1624,18 +1744,18 @@ int s390_process_IRQ( unsigned int irq, * Check whether we must issue a SENSE CCW ourselves if there is no * concurrent sense facility installed for the subchannel. * - * Note: We should check for ioinfo[irq].ui.flags.consns but VM + * Note: We should check for ioinfo[irq]->ui.flags.consns but VM * violates the ESA/390 architecture and doesn't present an * operand exception for virtual devices without concurrent * sense facility available/supported when enabling the * concurrent sense facility. */ - if ( ( ( ioinfo[irq].devstat.ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK ) + if ( ( ( ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK ) && ( !issense ) ) - || ( ioinfo[irq].ui.flags.delsense && ending_status ) ) + || ( ioinfo[irq]->ui.flags.delsense && ending_status ) ) { int ret_io; - ccw1_t *s_ccw = &ioinfo[irq].senseccw; + ccw1_t *s_ccw = &ioinfo[irq]->senseccw; unsigned long s_flag = 0; if (ending_status) @@ -1646,12 +1766,12 @@ int s390_process_IRQ( unsigned int irq, * sensing. When finally calling the IRQ handler we must not overlay * the original device status but copy the sense data only. */ - memcpy( irq_desc[irq].action->dev_id, - &(ioinfo[irq].devstat), + memcpy( ioinfo[irq]->irq_desc.action->dev_id, + &(ioinfo[irq]->devstat), sizeof( devstat_t) ); s_ccw->cmd_code = CCW_CMD_BASIC_SENSE; - s_ccw->cda = (char *)virt_to_phys( ioinfo[irq].devstat.ii.sense.data); + s_ccw->cda = (char *)virt_to_phys( ioinfo[irq]->devstat.ii.sense.data); s_ccw->count = SENSE_MAX_COUNT; s_ccw->flags = CCW_FLAG_SLI; @@ -1659,7 +1779,7 @@ int s390_process_IRQ( unsigned int irq, * If free_irq() or a sync do_IO/s390_start_IO() is in * process we have to sense synchronously */ - if ( ioinfo[irq].ui.flags.unready || ioinfo[irq].ui.flags.syncio ) + if ( ioinfo[irq]->ui.flags.unready || ioinfo[irq]->ui.flags.syncio ) { s_flag = DOIO_WAIT_FOR_INTERRUPT; @@ -1679,16 +1799,16 @@ int s390_process_IRQ( unsigned int irq, */ allow4handler = 0; - ioinfo[irq].ui.flags.fast = 0; - ioinfo[irq].ui.flags.repall = 0; - ioinfo[irq].ui.flags.w4final = 0; - ioinfo[irq].ui.flags.delsense = 0; + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + ioinfo[irq]->ui.flags.delsense = 0; - ioinfo[irq].devstat.cstat = 0; - ioinfo[irq].devstat.dstat = 0; - ioinfo[irq].devstat.rescnt = SENSE_MAX_COUNT; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.rescnt = SENSE_MAX_COUNT; - ioinfo[irq].ui.flags.w4sense = 1; + ioinfo[irq]->ui.flags.w4sense = 1; ret_io = s390_start_IO( irq, s_ccw, @@ -1705,10 +1825,10 @@ int s390_process_IRQ( unsigned int irq, * intermediate status to the device interrupt * handler. */ - ioinfo[irq].ui.flags.fast = 0; - ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq].ui.flags.delsense = 1; + ioinfo[irq]->ui.flags.delsense = 1; allow4handler = 0; } /* endif */ @@ -1727,11 +1847,12 @@ int s390_process_IRQ( unsigned int irq, if ( allow4handler ) { allow4handler = ending_status - || ( ioinfo[irq].ui.flags.repall ) - || ( ioinfo[irq].devstat.ii.irb.scsw.cstat & SCHN_STAT_PCI ) - || ( (ioinfo[irq].ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) ) - || ( ioinfo[irq].ui.flags.oper == 0 ); - } + || ( ioinfo[irq]->ui.flags.repall ) + || ( ioinfo[irq]->devstat.ii.irb.scsw.cstat & SCHN_STAT_PCI ) + || ( (ioinfo[irq]->ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) ) + || ( ioinfo[irq]->ui.flags.oper == 0 ); + + } /* endif */ /* * We used to copy the device status information right before @@ -1749,9 +1870,9 @@ int s390_process_IRQ( unsigned int irq, * bytes only as the original status information was * saved prior to sense already. */ - if ( ioinfo[irq].ui.flags.w4sense ) + if ( ioinfo[irq]->ui.flags.w4sense ) { - int sense_count = SENSE_MAX_COUNT-ioinfo[irq].devstat.rescnt; + int sense_count = SENSE_MAX_COUNT-ioinfo[irq]->devstat.rescnt; #if CONFIG_DEBUG_IO if ( irq != cons_dev ) @@ -1759,27 +1880,32 @@ int s390_process_IRQ( unsigned int irq, "BASIC SENSE bytes avail %d\n", irq, sense_count ); #endif - ioinfo[irq].ui.flags.w4sense = 0; + ioinfo[irq]->ui.flags.w4sense = 0; ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL; ((devstat_t *)(action->dev_id))->scnt = sense_count; if (sense_count >= 0) + { memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data, - &(ioinfo[irq].devstat.ii.sense.data), sense_count); + &(ioinfo[irq]->devstat.ii.sense.data), + sense_count); + } else -#if 0 + { +#if 1 panic( "s390_process_IRQ(%04x) encountered " "negative sense count\n", irq); #else - printk( "s390_process_IRQ(%04x) encountered " + printk( KERN_CRIT"s390_process_IRQ(%04x) encountered " "negative sense count\n", irq); #endif + } /* endif */ } else { - memcpy( action->dev_id, &(ioinfo[irq].devstat), sdevstat ); + memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat ); } /* endif */ @@ -1788,9 +1914,10 @@ int s390_process_IRQ( unsigned int irq, /* * for status pending situations other than deferred interrupt * conditions detected by s390_process_IRQ() itself we must not - * call the handler. + * call the handler. This will synchronously be reported back + * to the caller instead, e.g. when detected during do_IO(). */ - if ( ioinfo[irq].ui.flags.s_pend ) + if ( ioinfo[irq]->ui.flags.s_pend ) allow4handler = 0; /* @@ -1803,18 +1930,18 @@ int s390_process_IRQ( unsigned int irq, * We only reset the busy condition when we are sure that no further * interrupt is pending for the current I/O request (ending_status). */ - if ( ending_status || !ioinfo[irq].ui.flags.oper ) + if ( ending_status || !ioinfo[irq]->ui.flags.oper ) { - ioinfo[irq].ui.flags.oper = 1; /* dev IS oper */ + ioinfo[irq]->ui.flags.oper = 1; /* dev IS oper */ - ioinfo[irq].ui.flags.busy = 0; - ioinfo[irq].ui.flags.doio = 0; - ioinfo[irq].ui.flags.haltio = 0; - ioinfo[irq].ui.flags.fast = 0; - ioinfo[irq].ui.flags.repall = 0; - ioinfo[irq].ui.flags.w4final = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.haltio = 0; + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; - ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FINAL_STATUS; action->handler( irq, action->dev_id, ®s); @@ -1823,23 +1950,23 @@ int s390_process_IRQ( unsigned int irq, // reset intparm after final status or we will badly present unsolicited // interrupts with a intparm value possibly no longer valid. // - ioinfo[irq].devstat.intparm = 0; + ioinfo[irq]->devstat.intparm = 0; // // Was there anything queued ? Start the pending channel program // if there is one. // - if ( ioinfo[irq].ui.flags.doio_q ) + if ( ioinfo[irq]->ui.flags.doio_q ) { int ret; ret = s390_start_IO( irq, - ioinfo[irq].qcpa, - ioinfo[irq].qintparm, - ioinfo[irq].qlpm, - ioinfo[irq].qflag); + ioinfo[irq]->qcpa, + ioinfo[irq]->qintparm, + ioinfo[irq]->qlpm, + ioinfo[irq]->qflag); - ioinfo[irq].ui.flags.doio_q = 0; + ioinfo[irq]->ui.flags.doio_q = 0; /* * If s390_start_IO() failed call the device's interrupt @@ -1858,7 +1985,7 @@ int s390_process_IRQ( unsigned int irq, } else { - ioinfo[irq].ui.flags.w4final = 1; + ioinfo[irq]->ui.flags.w4final = 1; action->handler( irq, action->dev_id, ®s); } /* endif */ @@ -1869,16 +1996,16 @@ int s390_process_IRQ( unsigned int irq, case 3: /* device not operational */ - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; - ioinfo[irq].ui.flags.busy = 0; - ioinfo[irq].ui.flags.doio = 0; - ioinfo[irq].ui.flags.haltio = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.haltio = 0; - ioinfo[irq].devstat.cstat = 0; - ioinfo[irq].devstat.dstat = 0; - ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; /* * When we find a device "not oper" we save the status @@ -1891,7 +2018,8 @@ int s390_process_IRQ( unsigned int irq, * interrupts on "not oper" conditions. */ - if ( ioinfo[irq].ui.flags.fast && ioinfo[irq].ui.flags.w4final ) + if ( ( ioinfo[irq]->ui.flags.fast ) + && ( ioinfo[irq]->ui.flags.w4final ) ) { /* * If a new request was queued already, we have @@ -1899,23 +2027,23 @@ int s390_process_IRQ( unsigned int irq, * queued request by switching the "intparm" value * and notify the interrupt handler. */ - if ( ioinfo[irq].ui.flags.doio_q ) + if ( ioinfo[irq]->ui.flags.doio_q ) { - ioinfo[irq].devstat.intparm = ioinfo[irq].qintparm; + ioinfo[irq]->devstat.intparm = ioinfo[irq]->qintparm; } /* endif */ } /* endif */ - ioinfo[irq].ui.flags.fast = 0; - ioinfo[irq].ui.flags.repall = 0; - ioinfo[irq].ui.flags.w4final = 0; + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; - memcpy( action->dev_id, &(ioinfo[irq].devstat), sdevstat ); + memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat ); - ioinfo[irq].devstat.intparm = 0; + ioinfo[irq]->devstat.intparm = 0; - if ( !ioinfo[irq].ui.flags.s_pend ) + if ( !ioinfo[irq]->ui.flags.s_pend ) action->handler( irq, action->dev_id, ®s); ending_status = 1; @@ -1958,24 +2086,28 @@ int set_cons_dev( int irq ) { rc = -ENODEV; } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } else { /* * modify the indicated console device to operate * on special console interrupt sublass 7 */ - ccode = stsch( irq, &(ioinfo[irq].schib) ); + ccode = stsch( irq, &(ioinfo[irq]->schib) ); if (ccode) { rc = -ENODEV; - ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; } else { - ioinfo[irq].schib.pmcw.isc = 7; + ioinfo[irq]->schib.pmcw.isc = 7; - ccode = msch( irq, &(ioinfo[irq].schib) ); + ccode = msch( irq, &(ioinfo[irq]->schib) ); if (ccode) { @@ -2015,25 +2147,29 @@ int reset_cons_dev( int irq) { rc = -ENODEV; } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } else { /* * reset the indicated console device to operate * on default console interrupt sublass 3 */ - ccode = stsch( irq, &(ioinfo[irq].schib) ); + ccode = stsch( irq, &(ioinfo[irq]->schib) ); if (ccode) { rc = -ENODEV; - ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; } else { - ioinfo[irq].schib.pmcw.isc = 3; + ioinfo[irq]->schib.pmcw.isc = 3; - ccode = msch( irq, &(ioinfo[irq].schib) ); + ccode = msch( irq, &(ioinfo[irq]->schib) ); if (ccode) { @@ -2071,7 +2207,7 @@ int wait_cons_dev( int irq ) * before entering the spinlock we may already have * processed the interrupt on a different CPU ... */ - if ( ioinfo[irq].ui.flags.busy == 1 ) + if ( ioinfo[irq]->ui.flags.busy == 1 ) { long cr6 __attribute__ ((aligned (8))); @@ -2086,15 +2222,14 @@ int wait_cons_dev( int irq ) do { tpi_info_t tpi_info; if (tpi(&tpi_info) == 1) { - s390_process_IRQ(tpi_info.irq, - tpi_info.intparm); + s390_process_IRQ( tpi_info.irq ); } else { s390irq_spin_unlock(irq); tod_wait(100); s390irq_spin_lock(irq); } eieio(); - } while (ioinfo[irq].ui.flags.busy == 1); + } while (ioinfo[irq]->ui.flags.busy == 1); /* * restore previous isc value @@ -2125,17 +2260,17 @@ int enable_cpu_sync_isc( int irq ) int count = 0; int rc = 0; - if ( irq <= highest_subchannel ) + if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA ) { - ccode = stsch( irq, &(ioinfo[irq].schib) ); + ccode = stsch( irq, &(ioinfo[irq]->schib) ); if ( !ccode ) { - ioinfo[irq].schib.pmcw.isc = 5; + ioinfo[irq]->schib.pmcw.isc = 5; do { - ccode = msch( irq, &(ioinfo[irq].schib) ); + ccode = msch( irq, &(ioinfo[irq]->schib) ); if (ccode == 0 ) { @@ -2162,11 +2297,11 @@ int enable_cpu_sync_isc( int irq ) // // process pending status // - ioinfo[irq].ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.s_pend = 1; - s390_process_IRQ( irq, 0 ); + s390_process_IRQ( irq ); - ioinfo[irq].ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.s_pend = 0; count++; @@ -2201,13 +2336,13 @@ int disable_cpu_sync_isc( int irq) int ccode; long cr6 __attribute__ ((aligned (8))); - if ( irq <= highest_subchannel ) + if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA ) { - ccode = stsch( irq, &(ioinfo[irq].schib) ); + ccode = stsch( irq, &(ioinfo[irq]->schib) ); - ioinfo[irq].schib.pmcw.isc = 3; + ioinfo[irq]->schib.pmcw.isc = 3; - ccode = msch( irq, &(ioinfo[irq].schib) ); + ccode = msch( irq, &(ioinfo[irq]->schib) ); if (ccode) { @@ -2236,8 +2371,15 @@ int disable_cpu_sync_isc( int irq) return( rc); } -void VM_virtual_device_info( int devno, /* device number */ - senseid_t *ps ) /* pointer to sense ID data area */ +// +// Input : +// devno - device number +// ps - pointer to sense ID data area +// +// Output : none +// +void VM_virtual_device_info( unsigned int devno, + senseid_t *ps ) { diag210_t diag_data; int ccode; @@ -2269,7 +2411,7 @@ void VM_virtual_device_info( int devno, /* device number break; - case 40: + case 0x40: switch (diag_data.vrdcvtyp) { case 0xC0: @@ -2537,9 +2679,13 @@ int read_dev_chars( int irq, void **buffer, int length ) { return( -ENODEV ); - } /* endif */ + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } - if ( ioinfo[irq].ui.flags.oper == 0 ) + if ( ioinfo[irq]->ui.flags.oper == 0 ) { return( -ENODEV ); @@ -2556,11 +2702,13 @@ int read_dev_chars( int irq, void **buffer, int length ) __save_flags(flags); __cli(); - rdc_ccw = &ioinfo[irq].senseccw; + rdc_ccw = &ioinfo[irq]->senseccw; - if ( !ioinfo[irq].ui.flags.ready ) + if ( !ioinfo[irq]->ui.flags.ready ) { - ret = request_irq( irq, init_IRQ_handler, 0, "RDC", &devstat ); + ret = request_irq( irq, + init_IRQ_handler, + 0, "RDC", &devstat ); if ( !ret ) { @@ -2601,7 +2749,7 @@ int read_dev_chars( int irq, void **buffer, int length ) 0, // n/a DOIO_WAIT_FOR_INTERRUPT ); retry--; - devflag = ((devstat_t *)(irq_desc[irq].action->dev_id))->flag; + devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag; } while ( ( retry ) && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); @@ -2650,10 +2798,14 @@ int read_conf_data( int irq, void **buffer, int *length ) if ( (irq > highest_subchannel) || (irq < 0 ) ) { return( -ENODEV ); + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); } /* endif */ - if ( ioinfo[irq].ui.flags.oper == 0 ) + if ( ioinfo[irq]->ui.flags.oper == 0 ) { return( -ENODEV ); @@ -2664,7 +2816,7 @@ int read_conf_data( int irq, void **buffer, int *length ) */ for ( ; (found == 0) && (ciw_cnt < 62); ciw_cnt++ ) { - if ( senseid[irq].ciw[ciw_cnt].ct == CIW_TYPE_RCD ) + if ( ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD ) { found = 1; break; @@ -2674,7 +2826,7 @@ int read_conf_data( int irq, void **buffer, int *length ) if ( found ) { - ccw1_t *rcd_ccw = &ioinfo[irq].senseccw; + ccw1_t *rcd_ccw = &ioinfo[irq]->senseccw; devstat_t devstat; char *rcd_buf; int devflag; @@ -2685,9 +2837,11 @@ int read_conf_data( int irq, void **buffer, int *length ) __save_flags(flags); __cli(); - if ( !ioinfo[irq].ui.flags.ready ) + if ( !ioinfo[irq]->ui.flags.ready ) { - ret = request_irq( irq, init_IRQ_handler, 0, "RCD", &devstat ); + ret = request_irq( irq, + init_IRQ_handler, + 0, "RCD", &devstat ); if ( !ret ) { @@ -2699,13 +2853,14 @@ int read_conf_data( int irq, void **buffer, int *length ) if ( !ret ) { - rcd_buf = kmalloc( senseid[irq].ciw[ciw_cnt].count, GFP_KERNEL); + rcd_buf = kmalloc( ioinfo[irq]->senseid.ciw[ciw_cnt].count, + GFP_KERNEL); do { - rcd_ccw->cmd_code = senseid[irq].ciw[ciw_cnt].cmd; + rcd_ccw->cmd_code = ioinfo[irq]->senseid.ciw[ciw_cnt].cmd; rcd_ccw->cda = (char *)virt_to_phys( rcd_buf ); - rcd_ccw->count = senseid[irq].ciw[ciw_cnt].count; + rcd_ccw->count = ioinfo[irq]->senseid.ciw[ciw_cnt].count; rcd_ccw->flags = CCW_FLAG_SLI; ret = s390_start_IO( irq, @@ -2716,7 +2871,7 @@ int read_conf_data( int irq, void **buffer, int *length ) retry--; - devflag = ((devstat_t *)(irq_desc[irq].action->dev_id))->flag; + devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag; } while ( ( retry ) && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); @@ -2733,7 +2888,7 @@ int read_conf_data( int irq, void **buffer, int *length ) */ if ( !ret ) { - *length = senseid[irq].ciw[ciw_cnt].count; + *length = ioinfo[irq]->senseid.ciw[ciw_cnt].count; *buffer = rcd_buf; } /* endif */ @@ -2751,16 +2906,111 @@ int read_conf_data( int irq, void **buffer, int *length ) } - int get_dev_info( int irq, dev_info_t *pdi) { return( get_dev_info_by_irq( irq, pdi) ); } + +static int __inline__ get_next_available_irq( ioinfo_t *pi) +{ + int ret_val; + + while ( TRUE ) + { + if ( pi->ui.flags.oper ) + { + ret_val = pi->irq; + break; + } + else + { + pi = pi->next; + + // + // leave at end of list unconditionally + // + if ( pi == NULL ) + { + ret_val = -ENODEV; + break; + } + + } /* endif */ + + } /* endwhile */ + + return ret_val; +} + + +int get_irq_first( void ) +{ + int ret_irq; + + if ( ioinfo_head ) + { + if ( ioinfo_head->ui.flags.oper ) + { + ret_irq = ioinfo_head->irq; + } + else if ( ioinfo_head->next ) + { + ret_irq = get_next_available_irq( ioinfo_head->next ); + + } + else + { + ret_irq = -ENODEV; + + } /* endif */ + } + else + { + ret_irq = -ENODEV; + + } /* endif */ + + return ret_irq; +} + +int get_irq_next( int irq ) +{ + int ret_irq; + + if ( ioinfo[irq] != INVALID_STORAGE_AREA ) + { + if ( ioinfo[irq]->next ) + { + if ( ioinfo[irq]->next->ui.flags.oper ) + { + ret_irq = ioinfo[irq]->next->irq; + } + else + { + ret_irq = get_next_available_irq( ioinfo[irq]->next ); + + } /* endif */ + } + else + { + ret_irq = -ENODEV; + + } /* endif */ + } + else + { + ret_irq = -EINVAL; + + } /* endif */ + + return ret_irq; +} + int get_dev_info_by_irq( int irq, dev_info_t *pdi) { - if ( irq > highest_subchannel ) + if ( irq > highest_subchannel || irq < 0 ) { return -ENODEV; } @@ -2768,24 +3018,35 @@ int get_dev_info_by_irq( int irq, dev_info_t *pdi) { return -EINVAL; } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } else { - pdi->devno = ioinfo[irq].schib.pmcw.dev; + pdi->devno = ioinfo[irq]->schib.pmcw.dev; pdi->irq = irq; - if ( ioinfo[irq].ui.flags.oper ) + if ( ioinfo[irq]->ui.flags.oper ) { pdi->status = 0; - memcpy( &(pdi->sid_data), &senseid[irq], sizeof( senseid_t)); + memcpy( &(pdi->sid_data), + &ioinfo[irq]->senseid, + sizeof( senseid_t)); } else { pdi->status = DEVSTAT_NOT_OPER; - memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t)); + memcpy( &(pdi->sid_data), + '\0', + sizeof( senseid_t)); pdi->sid_data.cu_type = 0xFFFF; } /* endif */ + if ( ioinfo[irq]->ui.flags.ready ) + pdi->status |= DEVSTAT_DEVICE_OWNED; + return 0; } /* endif */ @@ -2812,15 +3073,17 @@ int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi) for ( i=0; i <= highest_subchannel; i++ ) { - if ( ioinfo[i].schib.pmcw.dev == devno ) + if ( ioinfo[i] != INVALID_STORAGE_AREA + && ioinfo[i]->schib.pmcw.dev == devno ) { - if ( ioinfo[i].ui.flags.oper ) + if ( ioinfo[i]->ui.flags.oper ) { pdi->status = 0; pdi->irq = i; pdi->devno = devno; + memcpy( &(pdi->sid_data), - &senseid[i], + &ioinfo[i]->senseid, sizeof( senseid_t)); } else @@ -2828,11 +3091,15 @@ int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi) pdi->status = DEVSTAT_NOT_OPER; pdi->irq = i; pdi->devno = devno; + memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t)); pdi->sid_data.cu_type = 0xFFFF; } /* endif */ + if ( ioinfo[i]->ui.flags.ready ) + pdi->status |= DEVSTAT_DEVICE_OWNED; + rc = 0; /* found */ break; @@ -2855,7 +3122,9 @@ int get_irq_by_devno( unsigned int devno ) { for ( i=0; i <= highest_subchannel; i++ ) { - if ( devno == ioinfo[i].schib.pmcw.dev ) + if ( (ioinfo[i] != INVALID_STORAGE_AREA ) + && (ioinfo[i]->schib.pmcw.dev == devno) + && (ioinfo[i]->schib.pmcw.dnv == 1 ) ) { rc = i; break; @@ -2867,13 +3136,14 @@ int get_irq_by_devno( unsigned int devno ) } /* endif */ return( rc); - } unsigned int get_devno_by_irq( int irq ) { - if ( irq > highest_subchannel ) + if ( ( irq > highest_subchannel ) + || ( irq < 0 ) + || ( ioinfo[irq] == INVALID_STORAGE_AREA ) ) { return -1; @@ -2883,10 +3153,13 @@ unsigned int get_devno_by_irq( int irq ) * we don't need to check for the device be operational * as the initial STSCH will always present the device * number defined by the IOCDS regardless of the device - * existing or not. + * existing or not. However, there could be subchannels + * defined who's device number isn't valid ... */ - return( ioinfo[irq].schib.pmcw.dev ); - + if ( ioinfo[irq]->schib.pmcw.dnv ) + return( ioinfo[irq]->schib.pmcw.dev ); + else + return -1; } /* @@ -2907,10 +3180,11 @@ void s390_device_recognition( void) * We issue the SenseID command on I/O subchannels we think are * operational only. */ - if ( ( schiblock[irq].pmcw.st == 0 ) - && ( ioinfo[irq].ui.flags.oper == 1 ) ) + if ( ( ioinfo[irq] != INVALID_STORAGE_AREA ) + && ( ioinfo[irq]->schib.pmcw.st == 0 ) + && ( ioinfo[irq]->ui.flags.oper == 1 ) ) { - s390_SenseID( irq, &senseid[irq] ); + s390_SenseID( irq, &ioinfo[irq]->senseid ); } /* endif */ @@ -2930,7 +3204,6 @@ void s390_device_recognition( void) void s390_process_subchannels( void) { int isValid; - int irq = 0; /* Evaluate all subchannels starting with 0 ... */ do @@ -2939,7 +3212,7 @@ void s390_process_subchannels( void) irq++; - } while ( isValid && irq < NR_IRQS ); + } while ( isValid && irq < __MAX_SUBCHANNELS ); highest_subchannel = --irq; @@ -2959,45 +3232,120 @@ int s390_validate_subchannel( int irq ) int retry; /* retry count for status pending conditions */ int ccode; /* condition code for stsch() only */ int ccode2; /* condition code for other I/O routines */ + schib_t *p_schib; /* * The first subchannel that is not-operational (ccode==3) * indicates that there aren't any more devices available. */ - ccode = stsch( irq, &schiblock[irq]); + if ( ( init_IRQ_complete ) + && ( ioinfo[irq] != INVALID_STORAGE_AREA ) ) + { + p_schib = &ioinfo[irq]->schib; + } + else + { + p_schib = &init_schib; + + } /* endif */ + + ccode = stsch( irq, p_schib); if ( ccode == 0) { /* * ... just being curious we check for non I/O subchannels */ - if ( schiblock[irq].pmcw.st ) + if ( p_schib->pmcw.st ) { printk( "Subchannel %04X reports " "non-I/O subchannel type %04X\n", irq, - schiblock[irq].pmcw.st); + p_schib->pmcw.st); - ioinfo[irq].ui.flags.oper = 0; + if ( ioinfo[irq] != INVALID_STORAGE_AREA ) + ioinfo[irq]->ui.flags.oper = 0; } /* endif */ - if ( !schiblock[irq].pmcw.dnv ) + if ( p_schib->pmcw.dnv ) + { + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + + if ( !init_IRQ_complete ) { + ioinfo[irq] = + (ioinfo_t *)alloc_bootmem( sizeof(ioinfo_t)); + } + else + { + ioinfo[irq] = + (ioinfo_t *)kmalloc( sizeof(ioinfo_t), + GFP_KERNEL ); + + } /* endif */ + + memset( ioinfo[irq], '\0', sizeof( ioinfo_t)); + memcpy( &ioinfo[irq]->schib, + &init_schib, + sizeof( schib_t)); + ioinfo[irq]->irq_desc.status = IRQ_DISABLED; + ioinfo[irq]->irq_desc.handler = &no_irq_type; + /* - * don't process invalid device numbers ... + * We have to insert the new ioinfo element + * into the linked list, either at its head, + * its tail or insert it. */ - ioinfo[irq].ui.flags.oper = 0; + if ( ioinfo_head == NULL ) /* first element */ + { + ioinfo_head = ioinfo[irq]; + ioinfo_tail = ioinfo[irq]; } - else + else if (irq < ioinfo_head->irq) /* new head */ + { + ioinfo[irq]->next = ioinfo_head; + ioinfo_head->prev = ioinfo[irq]; + ioinfo_head = ioinfo[irq]; + } + else if (irq > ioinfo_tail->irq) /* new tail */ + { + ioinfo_tail->next = ioinfo[irq]; + ioinfo[irq]->prev = ioinfo_tail; + ioinfo_tail = ioinfo[irq]; + } + else /* insert element */ + { + ioinfo_t *pi = ioinfo_head; + + do + { + if ( irq < pi->next->irq ) { + ioinfo[irq]->next = pi->next; + ioinfo[irq]->prev = pi; + pi->next->prev = ioinfo[irq]; + pi->next = ioinfo[irq]; + break; + + } /* endif */ + + pi = pi->next; + + } while ( 1 ); + + } /* endif */ + + } /* endif */ + printk( "Detected device %04X on subchannel %04X" " - PIM = %02X, PAM = %02X, POM = %02X\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, irq, - schiblock[irq].pmcw.pim, - schiblock[irq].pmcw.pam, - schiblock[irq].pmcw.pom); + ioinfo[irq]->schib.pmcw.pim, + ioinfo[irq]->schib.pmcw.pam, + ioinfo[irq]->schib.pmcw.pom); /* * We now have to initially ... @@ -3009,41 +3357,41 @@ int s390_validate_subchannel( int irq ) * Note : we don't enable the device here, this is temporarily * done during device sensing below. */ - schiblock[irq].pmcw.isc = 3; /* could be smth. else ... */ - schiblock[irq].pmcw.csense = 1; /* concurrent sense */ - schiblock[irq].pmcw.ena = 0; /* force disable it */ - - if ( ( schiblock[irq].pmcw.pim != 0 ) - && ( schiblock[irq].pmcw.pim != 0x80 ) ) + ioinfo[irq]->schib.pmcw.isc = 3; /* could be smth. else */ + ioinfo[irq]->schib.pmcw.csense = 1; /* concurrent sense */ + ioinfo[irq]->schib.pmcw.ena = 0; /* force disable it */ + ioinfo[irq]->schib.pmcw.intparm = + ioinfo[irq]->schib.pmcw.dev; + + if ( ( ioinfo[irq]->schib.pmcw.pim != 0 ) + && ( ioinfo[irq]->schib.pmcw.pim != 0x80 ) ) { - schiblock[irq].pmcw.mp = 1; /* multipath mode */ + ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */ } /* endif */ /* * initialize ioinfo structure */ - ioinfo[irq].irq = irq; - ioinfo[irq].ui.flags.busy = 0; - ioinfo[irq].ui.flags.ready = 0; - ioinfo[irq].ui.flags.oper = 1; - ioinfo[irq].devstat.intparm = irq; - ioinfo[irq].devstat.devno = schiblock[irq].pmcw.dev; - - memcpy( &(ioinfo[irq].schib), &(schiblock[irq]), sizeof( schib_t)); + ioinfo[irq]->irq = irq; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.ready = 0; + ioinfo[irq]->ui.flags.oper = 1; + ioinfo[irq]->devstat.intparm = 0; + ioinfo[irq]->devstat.devno = ioinfo[irq]->schib.pmcw.dev; retry = 5; do { - ccode2 = msch_err( irq, &schiblock[irq]); + ccode2 = msch_err( irq, &ioinfo[irq]->schib); switch (ccode2) { case 0: // successful completion // // concurrent sense facility available ... // - ioinfo[irq].ui.flags.consns = 1; + ioinfo[irq]->ui.flags.consns = 1; break; case 1: // status pending @@ -3051,7 +3399,7 @@ int s390_validate_subchannel( int irq ) // How can we have a pending status as device is // disabled for interrupts ? Anyway, clear it ... // - tsch( irq, &(ioinfo[irq].devstat.ii.irb) ); + tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); retry--; break; @@ -3060,7 +3408,7 @@ int s390_validate_subchannel( int irq ) break; case 3: // not operational - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; retry = 0; break; @@ -3073,23 +3421,19 @@ int s390_validate_subchannel( int irq ) * re-issue the modify subchannel without trying to * enable the concurrent sense facility */ - schiblock[irq].pmcw.csense = 0; + ioinfo[irq]->schib.pmcw.csense = 0; - memcpy( &(ioinfo[irq].schib), - &(schiblock[irq]), - sizeof( schib_t)); - - ccode2 = msch_err( irq, &schiblock[irq]); + ccode2 = msch_err( irq, &ioinfo[irq]->schib); if ( ccode2 != 0 ) { printk( " ... modify subchannel (2) failed with CC = %X\n", ccode2 ); - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; } else { - ioinfo[irq].ui.flags.consns = 0; + ioinfo[irq]->ui.flags.consns = 0; } /* endif */ } @@ -3097,7 +3441,7 @@ int s390_validate_subchannel( int irq ) { printk( " ... modify subchannel (1) failed with CC = %X\n", ccode2); - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; } /* endif */ @@ -3156,15 +3500,19 @@ int s390_SenseID( int irq, senseid_t *sid ) { return( -ENODEV ); + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); } /* endif */ - if ( ioinfo[irq].ui.flags.oper == 0 ) + if ( ioinfo[irq]->ui.flags.oper == 0 ) { return( -ENODEV ); } /* endif */ - if ( !ioinfo[irq].ui.flags.ready ) + if ( !ioinfo[irq]->ui.flags.ready ) { /* * Perform SENSE ID command processing. We have to request device @@ -3188,10 +3536,10 @@ int s390_SenseID( int irq, senseid_t *sid ) sense_ccw.count = sizeof( senseid_t); sense_ccw.flags = CCW_FLAG_SLI; - senseid[irq].cu_type = 0xFFFF; /* initialize fields ... */ - senseid[irq].cu_model = 0; - senseid[irq].dev_type = 0; - senseid[irq].dev_model = 0; + ioinfo[irq]->senseid.cu_type = 0xFFFF; /* initialize fields ... */ + ioinfo[irq]->senseid.cu_model = 0; + ioinfo[irq]->senseid.dev_type = 0; + ioinfo[irq]->senseid.dev_model = 0; /* * We now issue a SenseID request. In case of BUSY @@ -3214,7 +3562,7 @@ int s390_SenseID( int irq, senseid_t *sid ) #if CONFIG_DEBUG_IO printk( "Device %04X on Subchannel %04X " "reports pending status, retry : %d\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, irq, retry); #endif @@ -3238,7 +3586,7 @@ int s390_SenseID( int irq, senseid_t *sid ) " retry %d, cnt %02d," " sns :" " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, retry, devstat.scnt, devstat.ii.sense.data[0], @@ -3257,7 +3605,7 @@ int s390_SenseID( int irq, senseid_t *sid ) { printk( "Device %04X on Subchannel %04X " "became 'not operational'\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, irq); retry = 0; @@ -3290,7 +3638,7 @@ int s390_SenseID( int irq, senseid_t *sid ) if ( ( sid->cu_type == 0xFFFF ) && ( MACHINE_IS_VM ) ) { - VM_virtual_device_info( schiblock[irq].pmcw.dev, + VM_virtual_device_info( ioinfo[irq]->schib.pmcw.dev, sid ); } /* endif */ @@ -3304,22 +3652,22 @@ int s390_SenseID( int irq, senseid_t *sid ) * consider the device "not operational". */ printk( "Unknown device %04X on subchannel %04X\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, irq); - ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq]->ui.flags.oper = 0; } /* endif */ /* * Issue device info message if unit was operational . */ - if ( ioinfo[irq].ui.flags.oper ) + if ( ioinfo[irq]->ui.flags.oper ) { if ( sid->dev_type != 0 ) { printk( "Device %04X reports: CU Type/Mod = %04X/%02X," " Dev Type/Mod = %04X/%02X\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, sid->cu_type, sid->cu_model, sid->dev_type, @@ -3329,7 +3677,7 @@ int s390_SenseID( int irq, senseid_t *sid ) { printk( "Device %04X reports:" " Dev Type/Mod = %04X/%02X\n", - schiblock[irq].pmcw.dev, + ioinfo[irq]->schib.pmcw.dev, sid->cu_type, sid->cu_model); @@ -3337,7 +3685,7 @@ int s390_SenseID( int irq, senseid_t *sid ) } /* endif */ - if ( ioinfo[irq].ui.flags.oper ) + if ( ioinfo[irq]->ui.flags.oper ) irq_ret = 0; else irq_ret = -ENODEV; @@ -3350,37 +3698,16 @@ int s390_SenseID( int irq, senseid_t *sid ) void do_crw_pending(void) { } -#ifdef CONFIG_READIPL_ENABLED + +/* added by Holger Smolinski for reipl support in reipl.S */ void -do_reipl ( int sch ) +reipl ( int sch ) { - static ccw1_t iplccw[2] = { - { CCW_CMD_READ_IPL, CCW_FLAG_CC, 24, 0x00000000 }, - { CCW_CMD_TIC , CCW_FLAG_CC, 0, 0x00000008 } - }; - static orb_t iplorb = { - 0, - }; - static psw_t psw_0 = {0,}; - static long cr6_0 = 0x0; - static psw_t psw_1 = {0,}; - static long cr6_1 = 0x0; int i; - /* First disable all Devices/IRQs */ for ( i = 0; i < highest_subchannel; i ++ ) { free_irq ( i, (void*)REIPL_DEVID_MAGIC ); } - /* re enable the one device */ - enable_subchannel (sch); - __asm__ __volatile__ ( "spx 0" ); - __asm__ __volatile__ ( "lpsw %0" :: "m" (psw_0) ); /* disable all */ - __asm__ __volatile__ ( "lctl 6,6,%0" : : "m" (cr6_0) ); - __asm__ __volatile__ ( "lr 1,%0\n" - "ssch %1\n" - :: "d" (sch), "m" (iplorb) : "1" ); - __asm__ __volatile__ ( "lctl 6,6,%0" :: "m" (cr6_1) ); - __asm__ __volatile__ ( "lpsw %0" :: "m" (psw_1) ); - __asm__ __volatile__ ( "lpsw 0" ); /* restart */ + do_reipl( 0x10000 | sch ); } -#endif + diff --git a/arch/s390/kernel/s390io.h b/arch/s390/kernel/s390io.h deleted file mode 100644 index 43b46776ee8b..000000000000 --- a/arch/s390/kernel/s390io.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * arch/s390/kernel/s390io.h - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Ingo Adlung (adlung@de.ibm.com) - */ - -#ifndef __s390io_h -#define __s390io_h - -/* - * Private data structure used by do_IO() - * - * Note : If bit flags are added, the "unused" value must be - * decremented accordingly ! - */ -typedef struct { - unsigned int irq; /* aka. subchannel number */ - union { - unsigned int info; - struct { - unsigned int busy : 1; /* device currently in use */ - unsigned int oper : 1; /* device is operational */ - unsigned int fast : 1; /* post with "channel end", ... */ - /* ... don't wait for "device end" */ - /* ... from do_IO() parameters */ - unsigned int ready : 1; /* interrupt handler registered */ - unsigned int haltio : 1; /* halt_IO in process */ - unsigned int doio : 1; /* do_IO in process */ - unsigned int doio_q : 1; /* do_IO queued - only possible ... */ - /* ... if 'fast' is set too */ - unsigned int w4final : 1; /* wait for final status, internally */ - /* ... used with 'fast' setting only */ - unsigned int repall : 1; /* report every interrupt status */ - unsigned int unready : 1; /* deregister irq handler in process */ - unsigned int d_disable : 1; /* delayed disabling required */ - unsigned int w4sense : 1; /* SENSE status pending */ - unsigned int syncio : 1; /* synchronous I/O requested */ - unsigned int consns : 1; /* concurrent sense is available */ - unsigned int delsense : 1; /* delayed SENSE required */ - unsigned int s_pend : 1; /* status pending condition */ - unsigned int unused : 16; /* unused */ - } __attribute__ ((packed)) flags; - } ui; - unsigned int lpm; /* logical path mask to be used ... */ - /* ... from do_IO() parms. Only ... */ - /* ... valid if vlpm is set too. */ - schib_t schib; /* subchannel information block */ - orb_t orb; /* operation request block */ - devstat_t devstat; /* device status */ - ccw1_t *qcpa; /* queued channel program */ - ccw1_t senseccw; /* ccw for sense command */ - unsigned int stctl; /* accumulated status control from irb */ - unsigned int qintparm; /* queued interruption parameter */ - unsigned long qflag; /* queued flags */ - unsigned char qlpm; /* queued logical path mask */ - - } __attribute__ ((aligned(8))) ioinfo_t; - -#define IOINFO_FLAGS_BUSY 0x80000000 -#define IOINFO_FLAGS_OPER 0x40000000 -#define IOINFO_FLAGS_FAST 0x20000000 -#define IOINFO_FLAGS_READY 0x10000000 -#define IOINFO_FLAGS_HALTIO 0x08000000 -#define IOINFO_FLAGS_DOIO 0x04000000 -#define IOINFO_FLAGS_DOIO_Q 0x02000000 -#define IOINFO_FLAGS_W4FINAL 0x01000000 -#define IOINFO_FLAGS_REPALL 0x00800000 - -#endif /* __s390io_h */ - diff --git a/arch/s390/kernel/s390ksyms.c b/arch/s390/kernel/s390ksyms.c deleted file mode 100644 index fd7e0bf1e434..000000000000 --- a/arch/s390/kernel/s390ksyms.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * arch/s390/kernel/s390ksyms.c - * - * S390 version - */ - -#include "irq.h" -#include "asm/string.h" - -EXPORT_SYMBOL(get_dev_info_by_irq); -EXPORT_SYMBOL(get_dev_info_by_devno); -EXPORT_SYMBOL(get_irq_by_devno); -EXPORT_SYMBOL(get_devno_by_irq); -EXPORT_SYMBOL(halt_IO); -EXPORT_SYMBOL(irq_desc); -EXPORT_SYMBOL(do_IO); -EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(init_mm); -EXPORT_SYMBOL(_oi_bitmap); -EXPORT_SYMBOL(_ni_bitmap); -EXPORT_SYMBOL(_zb_findmap); - diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index e145a1c3c27f..f302b4feee65 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -2,7 +2,7 @@ * arch/s390/kernel/setup.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com) * @@ -102,12 +102,15 @@ void cpu_init (void) #ifndef __SMP__ void machine_restart(char * __unused) { + reipl(S390_lowcore.ipl_device); +#if 0 if (MACHINE_IS_VM) { cpcmd("IPL", NULL, 0); } else { /* FIXME: how to reipl ? */ disabled_wait(2); } +#endif } void machine_halt(void) @@ -151,11 +154,9 @@ void tod_wait(unsigned long delay) __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { - unsigned long memory_start, memory_end; - char c = ' ', *to = command_line, *from = COMMAND_LINE; static unsigned int smptrap=0; - unsigned long delay = 0; - int len = 0; + unsigned long memory_start, memory_end; + char c, cn, *to, *from; if (smptrap) return; @@ -187,10 +188,6 @@ __initfunc(void setup_arch(char **cmdline_p, rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif - /* nasty stuff with PARMAREAs. we use head.S or parameterline - if (!MOUNT_ROOT_RDONLY) - root_mountflags &= ~MS_RDONLY; - */ memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ memory_end = MEMORY_SIZE; init_task.mm->start_code = PAGE_OFFSET; @@ -202,6 +199,9 @@ __initfunc(void setup_arch(char **cmdline_p, memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + c = ' '; + from = COMMAND_LINE; + to = command_line; for (;;) { /* * "mem=XXX[kKmM]" sets memsize != 32M @@ -219,6 +219,8 @@ __initfunc(void setup_arch(char **cmdline_p, } } if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) { + unsigned long delay; + if (to != command_line) to--; delay = simple_strtoul(from+9, &from, 0); if (*from == 's' || *from == 'S') { @@ -230,10 +232,13 @@ __initfunc(void setup_arch(char **cmdline_p, } tod_wait(delay); } - c = *(from++); - if (!c) + cn = *(from++); + if (!cn) break; - if (COMMAND_LINE_SIZE <= ++len) + if (cn == ' ' && c == ' ') + continue; /* remove additional spaces */ + c = cn; + if (to - command_line >= COMMAND_LINE_SIZE) break; *(to++) = c; } diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 4f495fd8fd8f..0ad9703d6173 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -2,15 +2,18 @@ * arch/s390/kernel/signal.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * - * based on PPC & i386 signal handling by Linus Torvalds & Gary Thomas + * Based on Intel version + * + * Copyright (C) 1991, 1992 Linus Torvalds * * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson */ #include + #include #include #include @@ -24,38 +27,36 @@ #include #include #include -#if CONFIG_BINFMT_TOC -#include -#endif -/* - * These are the flags in the MSR that the user is allowed to change - * by modifying the saved value of the MSR on the stack. SE and BE - * should not be in this list since gdb may want to change these. I.e, - * you should be able to step out of a signal handler to see what - * instruction executes next after the signal handler completes. - * Alternately, if you stepped into a signal handler, you should be - * able to continue 'til the next breakpoint from within the signal - * handler, even if the handler returns. - */ - -#define __390_TO_DO__ 0 -#define __390_NEEDED__ 0 #define DEBUG_SIG 0 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -int do_signal(struct pt_regs *reg,sigset_t *oldset); -extern int sys_wait4(pid_t pid, unsigned long *stat_addr, - int options, unsigned long *ru); +/* pretcode & sig are used to store the return addr on Intel + & the signal no as the first parameter we do this differently + using gpr14 & gpr2. */ +#define SIGFRAME_COMMON \ +__u8 callee_used_stack[__SIGNAL_FRAMESIZE]; \ +struct sigcontext sc; \ +sigregs sregs; \ +__u8 retcode[S390_SYSCALL_SIZE]; +typedef struct +{ + SIGFRAME_COMMON +} sigframe; +typedef struct +{ + SIGFRAME_COMMON + struct siginfo info; + struct ucontext uc; +} rt_sigframe; +asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); +asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); /* * Atomically swap in the new signal mask, and wait for a signal. @@ -81,10 +82,6 @@ asmlinkage int sys_sigsuspend(struct pt_regs * regs,int history0, int history1, } } - -// -// N.B. This hasn't been tested it just compiles easily -// asmlinkage int sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize) { sigset_t saveset, newset; @@ -103,8 +100,8 @@ asmlinkage int sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; - while (1) - { + + while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); if (do_signal(regs, &saveset)) @@ -112,28 +109,14 @@ asmlinkage int sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t } } -asmlinkage int sys_rt_sigreturn(unsigned long __unused) -{ - printk("sys_rt_sigreturn(): %s/%d not yet implemented.\n", - current->comm,current->pid); - do_exit(SIGSEGV); -} - asmlinkage int -sys_sigaltstack(const stack_t *uss, stack_t *uoss) -{ - struct pt_regs *regs = (struct pt_regs *) &uss; - return do_sigaltstack(uss, uoss, regs->gprs[15]); -} - - -asmlinkage int sys_sigaction(int sig, const struct old_sigaction *act,struct old_sigaction *oact) +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) { struct k_sigaction new_ka, old_ka; int ret; - if (act) - { + if (act) { old_sigset_t mask; if (verify_area(VERIFY_READ, act, sizeof(*act)) || __get_user(new_ka.sa.sa_handler, &act->sa_handler) || @@ -146,181 +129,268 @@ asmlinkage int sys_sigaction(int sig, const struct old_sigaction *act,struct old ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - if (!ret && oact) - { + if (!ret && oact) { if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || - - - __put_user((u32)old_ka.sa.sa_handler,&oact->sa_handler) || - __put_user((u32)old_ka.sa.sa_restorer, &oact->sa_restorer)) + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) return -EFAULT; __put_user(old_ka.sa.sa_flags, &oact->sa_flags); __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); } + return ret; } -/* - * When we have signals to deliver, we set up on the - * user stack, going down from the original stack pointer: - * a sigregs struct - * one or more sigcontext structs - * a gap of __SIGNAL_FRAMESIZE bytes - * - * Each of these things must be a multiple of 16 bytes in size. - * - * XXX ultimately we will have to stack up a siginfo and ucontext - * for each rt signal. - */ +asmlinkage int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + struct pt_regs *regs = (struct pt_regs *) &uss; + return do_sigaltstack(uss, uoss, regs->gprs[15]); +} -struct sigregs { - user_regs_struct user_regs; - unsigned short tramp[1]; - /* Programs using the rs6000/xcoff abi can save up to 19 gp regs - and 18 fp regs below sp before decrementing it. */ -}; -int sys_sigreturn(struct pt_regs *regs) +static int save_sigregs(struct pt_regs *regs,sigregs *sregs) { - struct sigcontext_struct *sc, sigctx; - struct sigregs *sr; - int ret; - user_regs_struct saved_regs; + int err; + s390_fp_regs fpregs; + + err = __copy_to_user(&sregs->regs,regs,sizeof(s390_regs_common)); + if(!err) + { + save_fp_regs(&fpregs); + err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs)); + } + return(err); + +} + +static int restore_sigregs(struct pt_regs *regs,sigregs *sregs) +{ + int err; + s390_fp_regs fpregs; + psw_t saved_psw=regs->psw; + err=__copy_from_user(regs,&sregs->regs,sizeof(s390_regs_common)); + if(!err) + { + regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| + (regs->psw.mask&PSW_MASK_DEBUGCHANGE); + regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| + (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); + err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs)); + if(!err) + restore_fp_regs(&fpregs); + } + return(err); +} + +static int +restore_sigcontext(struct sigcontext *sc, pt_regs *regs, + sigregs *sregs,sigset_t *set) +{ + unsigned int err; + + err=restore_sigregs(regs,sregs); + if(!err) + err=__copy_from_user(&set->sig,&sc->oldmask,SIGMASK_COPY_SIZE); + return(err); +} + +int sigreturn_common(struct pt_regs *regs,int framesize) +{ + sigframe *frame = (sigframe *)regs->gprs[15]; sigset_t set; - unsigned long prevsp; - routine_descriptor tempdes; - sc = (struct sigcontext_struct *)(regs->gprs[15] + __SIGNAL_FRAMESIZE); - if (copy_from_user(&sigctx, sc, sizeof(sigctx))) - goto badframe; - memcpy(&set.sig[0],&sigctx.oldmask[0],SIGMASK_COPY_SIZE); + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + return -1; + if (restore_sigcontext(&frame->sc,regs,&frame->sregs,&set)) + return -1; sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); + return 0; +} - sc++; /* Look at next sigcontext */ - if (sc == (struct sigcontext_struct *)(sigctx.regs)) { - /* Last stacked signal - restore registers */ - sr = (struct sigregs *) sigctx.regs; - if (copy_from_user(&saved_regs, &sr->user_regs, - sizeof(sr->user_regs))) - goto badframe; - saved_regs.psw.mask=(regs->psw.mask&~PSW_MASK_DEBUGCHANGE) - |(saved_regs.psw.mask&PSW_MASK_DEBUGCHANGE); - saved_regs.psw.addr=(regs->psw.addr&~PSW_ADDR_DEBUGCHANGE) - |(saved_regs.psw.addr&PSW_ADDR_DEBUGCHANGE); - memcpy(regs,&saved_regs,sizeof(s390_regs)); - restore_fp_regs(&saved_regs.fp_regs); - ret = regs->gprs[2]; +asmlinkage int sys_sigreturn(struct pt_regs *regs) +{ - } else { - /* More signals to go */ - regs->gprs[15] = (unsigned long)sc - __SIGNAL_FRAMESIZE; - if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + if (sigreturn_common(regs,sizeof(sigframe))) goto badframe; - sr = (struct sigregs *) sigctx.regs; - regs->gprs[2] = ret = sigctx.signal; -#if DEBUG_SIG - regs->gprs[3] = (unsigned long) sr; // My best guess says this is used for debugging only DJB -#endif - regs->gprs[14] = (unsigned long) sr->tramp; // return address + return regs->gprs[2]; - if(uses_routine_descriptors()) - { - if(copy_from_user(&tempdes,(char *)sigctx.handler,sizeof(tempdes))) - goto badframe; - fix_routine_descriptor_regs(&tempdes,regs); +badframe: + force_sig(SIGSEGV, current); + return 0; } - else - regs->psw.addr = FIX_PSW(sigctx.handler); - if (get_user(prevsp, &sr->user_regs.gprs[15]) - || put_user(prevsp, (unsigned long *) regs->gprs[15])) +asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) +{ + rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; + stack_t st; + + if (sigreturn_common(regs,sizeof(rt_sigframe))) goto badframe; - } - return ret; + 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->gprs[15]); + return regs->gprs[2]; badframe: - lock_kernel(); - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); + return 0; } - /* * Set up a signal frame. */ -void setup_frame(struct pt_regs *regs, struct sigregs *frame,unsigned long newsp) + + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) { - struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp; - s390_fp_regs fpregs; + unsigned long sp; - routine_descriptor tempdes,*tempdesptr; + /* Default to using normal stack */ + sp = regs->gprs[15]; - if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) - goto badframe; - save_fp_regs(&fpregs); - if (__copy_to_user(&frame->user_regs, regs, sizeof(s390_regs)) - || __copy_to_user(&frame->user_regs.fp_regs,&fpregs,sizeof(fpregs)) - || __put_user(0x0A77, &frame->tramp[0])) /* SVC 0x77 */ - goto badframe; - newsp -= __SIGNAL_FRAMESIZE; - if (put_user(regs->gprs[15], (unsigned long *)newsp) || get_user(regs->gprs[2], &sc->signal)) - goto badframe; + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } - if(uses_routine_descriptors()) - { - if(get_user(tempdesptr, (routine_descriptor **)& sc->handler) - || copy_from_user(&tempdes,tempdesptr,sizeof(tempdes))) - goto badframe; - fix_routine_descriptor_regs(&tempdes,regs); + /* This is the legacy signal stack switching. */ + else if (!user_mode(regs) && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + sp = (unsigned long) ka->sa.sa_restorer; } - else + + return (void *)((sp - frame_size) & -8ul); +} + +static void *setup_frame_common(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs, + int frame_size,u16 retcode) { - if(get_user(regs->psw.addr, (void **) & sc->handler)) - goto badframe; - regs->psw.addr=FIX_PSW(regs->psw.addr); + sigframe *frame; + int err; + + frame = get_sigframe(ka, regs,frame_size); + if (!access_ok(VERIFY_WRITE, frame,frame_size)) + return 0; + err = save_sigregs(regs,&frame->sregs); + if(!err) + err=__put_user(&frame->sregs,&frame->sc.sregs); + if(!err) + + err=__copy_to_user(&frame->sc.oldmask,&set->sig,SIGMASK_COPY_SIZE); + if(!err) + { + regs->gprs[2]=(current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig); + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + } + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(retcode, (u16 *)(frame->retcode)); } - regs->gprs[15] = newsp; + return(err ? 0:frame); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + + if(!setup_frame_common(sig,ka,set,regs,sizeof(sigframe), + (S390_SYSCALL_OPCODE|__NR_sigreturn))) + goto give_sigsegv; #if DEBUG_SIG - regs->gprs[3] = (unsigned long) frame; + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); #endif - regs->gprs[14] = (unsigned long) frame->tramp; - return; -badframe: +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) +{ + rt_sigframe *frame; + addr_t orig_sp=regs->gprs[15]; + int err; + + if((frame=setup_frame_common(sig,ka,set,regs,sizeof(rt_sigframe), + (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + goto give_sigsegv; + + err = __copy_to_user(&frame->info, info, sizeof(*info)); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(orig_sp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + regs->gprs[3] = (u32)&frame->info; + regs->gprs[4] = (u32)&frame->uc; + + if (err) + goto give_sigsegv; + #if DEBUG_SIG - printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", - regs, frame, newsp); + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); #endif - lock_kernel(); - do_exit(SIGSEGV); + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); } /* * OK, we're invoking a handler */ -void -handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, - unsigned long *newspp, unsigned long frame) -{ - struct sigcontext_struct *sc; - if (regs->trap == 0x20) /* System Call! */ - { - switch(regs->gprs[2]) +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { + /* Are we from a system call? */ + if (regs->orig_gpr2 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->gprs[2]) { case -ERESTARTNOHAND: regs->gprs[2] = -EINTR; break; + case -ERESTARTSYS: - if (!(ka->sa.sa_flags & SA_RESTART)) - { + if (!(ka->sa.sa_flags & SA_RESTART)) { regs->gprs[2] = -EINTR; break; } @@ -328,20 +398,14 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, case -ERESTARTNOINTR: regs->gprs[2] = regs->orig_gpr2; regs->psw.addr -= 2; - /* Back up & retry system call */ } } - /* Put another sigcontext on the stack */ - *newspp -= sizeof(*sc); - sc = (struct sigcontext_struct *) *newspp; - if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) - goto badframe; - if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) - || copy_to_user(&sc->oldmask[0],&oldset->sig[0],SIGMASK_COPY_SIZE) - || __put_user((u32)((struct pt_regs *)frame), &sc->regs) - || __put_user(sig, &sc->signal)) - goto badframe; + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; @@ -353,35 +417,34 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } - return; - -badframe: -#if DEBUG_SIG - printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", - regs, frame, *newspp); - printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); -#endif - lock_kernel(); - do_exit(SIGSEGV); } - /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. + * + * 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; struct k_sigaction *ka; - unsigned long frame, newsp; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; if (!oldset) oldset = ¤t->blocked; - newsp = frame = regs->gprs[15] - sizeof(struct sigregs); - for (;;) { unsigned long signr; @@ -479,41 +542,19 @@ int do_signal(struct pt_regs *regs,sigset_t *oldset) } /* Whee! Actually deliver the signal. */ - handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); + handle_signal(signr, ka, &info, oldset, regs); + return 1; } - if ( - regs->trap == 0x20 /* System Call! */ && - ((int)regs->gprs[2] == -ERESTARTNOHAND || - (int)regs->gprs[2] == -ERESTARTSYS || - (int)regs->gprs[2] == -ERESTARTNOINTR)) { + /* Did we come from a system call? */ + if ( regs->trap == 0x20 /* System Call! */ ) { + /* Restart the system call - no handlers present */ + if (regs->gprs[2] == -ERESTARTNOHAND || + regs->gprs[2] == -ERESTARTSYS || + regs->gprs[2] == -ERESTARTNOINTR) { regs->gprs[2] = regs->orig_gpr2; - regs->psw.addr -= 2; /* Back up & retry system call */ + regs->psw.addr -= 2; } - - if (newsp == frame) - return 0; /* no signals delivered */ - - setup_frame(regs, (struct sigregs *) frame, newsp); - return 1; } - - - - - - - - - - - - - - - - - - - - + return 0; +} diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 8d4e7b095094..1cef8150878a 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -2,7 +2,7 @@ * arch/s390/kernel/smp.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), * Martin Schwidefsky (schwidefsky@de.ibm.com) * @@ -33,7 +33,7 @@ #include #include "cpcmd.h" -#include "irq.h" +#include /* prototypes */ extern void update_one_process( struct task_struct *p, @@ -85,12 +85,15 @@ void __init smp_setup(char *str, int *ints) void do_machine_restart(void) { smp_send_stop(); + reipl(S390_lowcore.ipl_device); +#if 0 if (MACHINE_IS_VM) { cpcmd("IPL", NULL, 0); } else { /* FIXME: how to reipl ? */ disabled_wait(2); } +#endif } void machine_restart(char * __unused) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 18604f1bcc22..18dd40942236 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -31,8 +31,6 @@ #include #include -#include "irq.h" - extern volatile unsigned long lost_ticks; diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index ec8285ef3a2c..8e84fc13cba7 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -2,7 +2,7 @@ * arch/s390/kernel/traps.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), * @@ -255,7 +255,7 @@ asmlinkage void illegal_op(struct pt_regs * regs, long error_code) get_user(*((__u16 *) opcode), location); else *((__u16 *)opcode)=*((__u16 *)location); - if(*((__u16 *)opcode)==BREAKPOINT_U16) + if(*((__u16 *)opcode)==S390_BREAKPOINT_U16) { if(do_debugger_trap(regs,SIGTRAP)) do_sig=1; @@ -443,6 +443,8 @@ __initfunc(void trap_init(void)) pgm_check_table[4] = &do_page_fault; pgm_check_table[0x10] = &do_page_fault; pgm_check_table[0x11] = &do_page_fault; + pgm_check_table[0x1C] = &privileged_op; + } @@ -456,6 +458,13 @@ void handle_per_exception(struct pt_regs *regs) per_info->lowcore.words.access_id=S390_lowcore.per_access_id; } if(do_debugger_trap(regs,SIGTRAP)) + { + /* I've seen this possibly a task structure being reused ? */ printk("Spurious per exception detected\n"); + printk("switching off per tracing for this task.\n"); + show_crashed_task_info(); + /* Hopefully switching off per tracing will help us survive */ + regs->psw.mask &= ~PSW_PER_MASK; + } } diff --git a/arch/s390/tools/dasdfmt/dasdfmt.8 b/arch/s390/tools/dasdfmt/dasdfmt.8 new file mode 100644 index 000000000000..b082443223bc --- /dev/null +++ b/arch/s390/tools/dasdfmt/dasdfmt.8 @@ -0,0 +1,74 @@ +.TH DASDFMT 8 "Tue Jan 25 2000" +.UC 4 +.SH NAME +dasdfmt \- formatting of DSAD (ECKD) disk drives. +.SH SYNOPSIS +\fBdasdfmt\fR [-tvyV] [-b \fIblockSize\fR] [\fIblockRange\fI] + \fIdiskSpec\fR +.SH DESCRIPTION +\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it +for usage with Linux for S/390. \fBWARNING\fR: Incautious usage of +\fBdasdfmt\fR can result in \fBLOSS OF DATA\fR. + +.SH OPTIONS +.TP +\fB-t\fR +Disables any modification of the disk drive. \fBdasdfmt\fR just prints +out, what it \fBwould\fR do. + +.TP +\fB-v\fR +Increases verbosity. + +.TP +\fB-y\fR +Start formatting without further user-confirmation. + +.TP +\fB-V\fR +Print version number and exit. + +.TP +\fB-b\fR \fIblockSize\fR +Specify blocksize to be used. \fIblocksize\fR must be a positive integer +and always be a power of two. Due due some limitations in the driver, +it is \fBstrongly\fR recommended to use a \fIblockSize\fR of \fI4096\fR. + +.TP +\fIblockRange\fR +This parameter specifies the number of the first and last block to be +formatted. If this parameter is \fBomitted\fR, formatting the \fBwhole\fR disk +is assumed. The \fIblockRange\fR can be specified in two different formats: +.sp + \fB-s\fR \fIstartBlock\fR \fB-e\fR \fIendBlock\fR +.br +or +.br + \fB-r\fR \fIstartBlock\fR-\fIendBlock\fR +.sp +If \fIstartBlock\fR is omitted, block \fB0\fR is assumed. If +\fIendBlock\fR is omitted, the last block of the disk is assumed. + +.TP +\fIdiskSpec\fR +This parameter specified the device to be formatted. It also can be +given in two variants: +.sp + \fB-f\fR \fB/dev/dd\fR\fIX\fR +.br +or +.br + \fB-n\fR \fIdevnum\fR +.sp +The first form uses the commonly used +.SM UNIX +device notation where \fIX\fR is a single lowercase letter. +The second form uses simply the VM vdev number. + +.SH BUGS +None so far ;-) + +.SH AUTHOR +.nf +This man-page was written by Fritz Elfert +.fi diff --git a/arch/s390/tools/dasdfmt/dasdfmt.c b/arch/s390/tools/dasdfmt/dasdfmt.c new file mode 100644 index 000000000000..cf975c2ee408 --- /dev/null +++ b/arch/s390/tools/dasdfmt/dasdfmt.c @@ -0,0 +1,639 @@ +/* + * + * dasdfmt.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Corporation + * Author(s): Utz Bacher, + * + * Device-in-use-checks by Fritz Elfert, + * + * Still to do: + * detect non-switch parameters ("dasdfmt -n 170 XY") and complain about them + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../drivers/s390/block/dasd.h" /* uses DASD_PARTN_BITS */ +#define __KERNEL__ /* we want to use kdev_t and not have to define it */ +#include +#undef __KERNEL__ + +#define EXIT_MISUSE 1 +#define EXIT_BUSY 2 +#define TEMPFILENAME "/tmp/ddfXXXXXX" +#define TEMPFILENAMECHARS 8 /* 8 characters are fixed in all temp filenames */ +#define IOCTL_COMMAND 'D' << 8 +#define SLASHDEV "/dev/" +#define PROC_DASD_DEVICES "/proc/dasd/devices" +#define DASD_DRIVER_NAME "dasd" +#define PROC_LINE_LENGTH 80 +#define ERR_LENGTH 80 + +#define MAX_FILELEN NAME_MAX+PATH_MAX + +#define GIVEN_DEVNO 1 +#define GIVEN_MAJOR 2 +#define GIVEN_MINOR 4 + +#define CHECK_START 1 +#define CHECK_END 2 +#define CHECK_BLKSIZE 4 +#define CHECK_ALL ~0 + +#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} +#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} + +#define CHECK_SPEC_MAX_ONCE(i,str) \ + {if (i>1) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "can only be specified once\n",prog_name);} + +#define PARSE_PARAM_INTO(x,param,base,str) \ + {x=(int)strtol(param,&endptr,base); \ + if (*endptr) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "is in invalid format\n",prog_name);} + +typedef struct { + int start_unit; + int stop_unit; + int blksize; +} format_data_t; + +char prog_name[]="dasd_format"; +char tempfilename[]=TEMPFILENAME; + +void +exit_usage(int exitcode) +{ + printf("Usage: %s [-htvyV] [-b blocksize] \n\n", + prog_name); + printf(" where is either\n"); + printf(" -s start_track -e end_track\n"); + printf(" or\n"); + printf(" -r start_track-end_track\n"); + printf(" and is either\n"); + printf(" -f /dev/ddX\n"); + printf(" or\n"); + printf(" -n \n"); + exit(exitcode); +} + +void +get_xno_from_xno(int *devno,kdev_t *major_no,kdev_t *minor_no,int mode) +{ + FILE *file; + int d,rc; + kdev_t mi,ma; + int mi_i,ma_i; /* for scanf :-( */ + char line[PROC_LINE_LENGTH]; + + file=fopen(PROC_DASD_DEVICES,"r"); + if (file==NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to open " \ + PROC_DASD_DEVICES ": %s (do you have the /proc " \ + "filesystem enabled?)\n",prog_name,strerror(errno)); + + fgets(line,sizeof(line),file); /* omit first line */ + while (fgets(line,sizeof(line),file)!=NULL) { + rc=sscanf(line,"%X%d%d",&d,&ma_i,&mi_i); + ma=ma_i; + mi=mi_i; + if ( (rc==3) && + !((d!=*devno)&&(mode&GIVEN_DEVNO)) && + !((ma!=*major_no)&&(mode&GIVEN_MAJOR)) && + !((mi!=*minor_no)&&(mode&GIVEN_MINOR)) ) { + *devno=d; + *major_no=ma; + *minor_no=mi; + /* yes, this is a quick exit, but the easiest way */ + fclose(file); + return; + } + } + fclose(file); + + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to find device in the /proc " \ + "filesystem (are you sure to have the right param line?)\n", + prog_name); +} + +char * +get_devname_from_devno(int devno,int verbosity) +{ + kdev_t major_no,minor_no; + kdev_t file_major,file_minor; + struct stat stat_buf; + int rc; + int found; + char *devname; + char tmpname[MAX_FILELEN]; + + DIR *dp; + struct dirent *direntp; + + /**** get minor number ****/ + get_xno_from_xno(&devno,&major_no,&minor_no,GIVEN_DEVNO); + + /**** get device file ****/ + if ((dp=opendir(SLASHDEV)) == NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: unable to read " SLASHDEV \ + "\n",prog_name); + found=0; + while ((direntp=readdir(dp)) != NULL) { + strcpy(tmpname,SLASHDEV); + strcat(tmpname,direntp->d_name); + rc=stat(tmpname,&stat_buf); + if (!rc) { + file_major=MAJOR(stat_buf.st_rdev); + file_minor=MINOR(stat_buf.st_rdev); + if ((file_major==major_no) && (file_minor==minor_no)) { + found=1; + break; + } + } + } + if (found) { + devname=malloc(strlen(direntp->d_name)); + strcpy(devname,tmpname); + } + rc=closedir(dp); + if (rc<0) ERRMSG("%s: unable to close directory " SLASHDEV \ + "; continuing\n",prog_name); + if (found) + return devname; + + if (verbosity>=1) + printf("I didn't find device node in " SLASHDEV \ + "; trying to create a temporary node\n"); + + /**** get temp file and create device node *****/ + rc=mkstemp(tempfilename); + if (rc==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to get temporary " \ + "filename: %s\n",prog_name,strerror(errno)); + close(rc); + rc=unlink(tempfilename); + + rc=mknod(tempfilename,S_IFBLK|0600,MKDEV(major_no,minor_no)); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to create temporary " \ + "device node %s: %s\n",prog_name,tempfilename, + strerror(errno)); + return tempfilename; +} + +char * +check_param(int mode,format_data_t data) +{ + char *s; + + if (NULL==(s=malloc(ERR_LENGTH))) + ERRMSG_EXIT(EXIT_FAILURE,"%s: not enough memory.\n",prog_name); + + if ((mode&CHECK_START)&&(data.start_unit<0)) { + strcpy(s,"start track must be greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.stop_unit<-1)) { + strcpy(s,"end track must be -1 or greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.start_unit>data.stop_unit)&& + (data.stop_unit!=-1)) { + strcpy(s,"end track must be higher than start track"); + goto exit; + } + + if ((mode&CHECK_BLKSIZE)&&(data.blksize<1)) { + strcpy(s,"blocksize must be a positive integer"); + goto exit; + } + if (mode&CHECK_BLKSIZE) while (data.blksize>0) { + if ((data.blksize%2)&&(data.blksize!=1)) { + strcpy(s,"blocksize must be a power of 2"); + goto exit; + } + data.blksize/=2; + } + + free(s); + return NULL; +exit: + return s; +} + +#define ASK_PRINTOUT printf("Please enter %s",output) +#define ASK_GETBUFFER fgets(buffer,sizeof(buffer),stdin) +#define ASK_SCANFORNUMBER(var) rc=sscanf(buffer,"%d%c",&var,&c) +#define ASK_COMPLAIN_FORMAT if ((rc==2)&&(c=='\n')) rc=1; \ + if (rc==-1) rc=1; /* this happens, if enter is pressed */ \ + if (rc!=1) printf(" -- wrong input, try again.\n") +#define ASK_CHECK_PARAM(mode) str=check_param(mode,params); \ + if (str!=NULL) { printf(" -- %s\n",str); rc=0; free(str); } + +format_data_t +ask_user_for_data(format_data_t params) +{ + char buffer[20]; /* should be enough for inputing track numbers */ + char c; + int i,rc; + char *str; + char output[60],o2[12]; + + i=params.start_unit; + do { + params.start_unit=i; + sprintf(output,"the start track of the range to format " \ + "[%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.start_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_START); + } while (rc!=1); + + i=params.stop_unit; + do { + params.stop_unit=i; + sprintf(output,"the end track of the range to format ["); + if (i==-1) sprintf(o2,"END]: "); else + sprintf(o2,"%d]: ",i); + strcat(output,o2); + ASK_PRINTOUT; + ASK_GETBUFFER; + if ( (!strcasecmp(buffer,"end")) || + (!strcasecmp(buffer,"end\n")) ) { + rc=1; + params.stop_unit=-1; + } else { + ASK_SCANFORNUMBER(params.stop_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_END); + } + } while (rc!=1); + + i=params.blksize; + do { + params.blksize=i; + sprintf(output,"the blocksize of the formatting [%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.blksize); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_BLKSIZE); + } while (rc!=1); + + return params; +} + +/* Check if the device we are going to format is mounted. + * If true, complain and exit. + */ +void +check_mounted(int major, int minor) +{ + FILE *f; + int ishift = 0; + struct mntent *ment; + struct stat stbuf; + char line[128]; + + /* If whole disk to be formatted ... */ + if ((minor % (1U << DASD_PARTN_BITS)) == 0) { + /* ... ignore partition-selector */ + minor >>= DASD_PARTN_BITS; + ishift = DASD_PARTN_BITS; + } + /* + * first, check filesystems + */ + if (!(f = fopen(_PATH_MOUNTED, "r"))) + ERRMSG_EXIT(EXIT_FAILURE, "%s: %s\n", _PATH_MOUNTED, + strerror(errno)); + while ((ment = getmntent(f))) { + if (stat(ment->mnt_fsname, &stbuf) == 0) + if ((major == MAJOR(stbuf.st_rdev)) && + (minor == (MINOR(stbuf.st_rdev)>>ishift))) { + ERRMSG("%s: device is mounted on %s!!\n", + prog_name,ment->mnt_dir); + ERRMSG_EXIT(EXIT_BUSY, "If you really want to " + "format it, please unmount it.\n"); + } + } + fclose(f); + /* + * second, check active swap spaces + */ + if (!(f = fopen("/proc/swaps", "r"))) + ERRMSG_EXIT(EXIT_FAILURE, "/proc/swaps: %s", strerror(errno)); + /* + * skip header line + */ + fgets(line, sizeof(line), f); + while (fgets(line, sizeof(line), f)) { + char *p; + for (p = line; *p && (!isspace(*p)); p++) ; + *p = '\0'; + if (stat(line, &stbuf) == 0) + if ((major == MAJOR(stbuf.st_rdev)) && + (minor == (MINOR(stbuf.st_rdev)>>ishift))) { + ERRMSG("%s: the device is in use for " + "swapping!!\n",prog_name); + ERRMSG_EXIT(EXIT_BUSY, "If you really want to " + "format it, please use swapoff %s.\n", + line); + } + } + fclose(f); +} + +void +do_format_dasd(char *dev_name,format_data_t format_params,int testmode, + int verbosity,int withoutprompt) +{ + int fd,rc; + struct stat stat_buf; + kdev_t minor_no,major_no; + int devno; + char inp_buffer[5]; /* to contain yes */ + + fd=open(dev_name,O_RDWR); + if (fd==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: error opening device %s: " \ + "%s\n",prog_name,dev_name,strerror(errno)); + + if (verbosity>=1) { + } + + rc=stat(dev_name,&stat_buf); + if (rc) { + ERRMSG_EXIT(EXIT_FAILURE,"%s: error occured during stat: " \ + "%s\n",prog_name,strerror(errno)); + } else { + if (!S_ISBLK(stat_buf.st_mode)) + ERRMSG_EXIT(EXIT_FAILURE,"%s: file is not a " \ + "blockdevice.\n",prog_name); + major_no=MAJOR(stat_buf.st_rdev); + minor_no=MINOR(stat_buf.st_rdev); + } + check_mounted(major_no, minor_no); + + if ( ((withoutprompt)&&(verbosity>=1)) || + (!withoutprompt) ) { + get_xno_from_xno(&devno,&major_no,&minor_no, + GIVEN_MAJOR|GIVEN_MINOR); + printf("\nI am going to format the device %s in the " \ + "following way:\n",dev_name); + printf(" Device number of device : 0x%x\n",devno); + printf(" Major number of device : %u\n",major_no); + printf(" Minor number of device : %u\n",minor_no); + printf(" Start track : %d\n" \ + ,format_params.start_unit); + printf(" End track : "); + if (format_params.stop_unit==-1) + printf("last track of disk\n"); + else + printf("%d\n",format_params.stop_unit); + printf(" Blocksize : %d\n" \ + ,format_params.blksize); + if (testmode) printf("Test mode active, omitting ioctl.\n"); + } + + while (!testmode) { + if (!withoutprompt) { + printf("\n--->> ATTENTION! <<---\n"); + printf("All data in the specified range of that " \ + "device will be lost.\nType yes to continue" \ + ", no will leave the disk untouched: "); + fgets(inp_buffer,sizeof(inp_buffer),stdin); + if (strcasecmp(inp_buffer,"yes") && + strcasecmp(inp_buffer,"yes\n")) { + printf("Omitting ioctl call (disk will " \ + "NOT be formatted).\n"); + break; + } + } + + if ( !( (withoutprompt)&&(verbosity<1) )) + printf("Formatting the device. This may take a " \ + "while (get yourself a coffee).\n"); + rc=ioctl(fd,IOCTL_COMMAND,format_params); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: the dasd driver " \ + "returned with the following error " \ + "message:\n%s\n",prog_name,strerror(errno)); + printf("Finished formatting the device.\n"); + + break; + } + + rc=close(fd); + if (rc) + ERRMSG("%s: error during close: " \ + "%s; continuing.\n",prog_name,strerror(errno)); +} + + + +int main(int argc,char *argv[]) { + int verbosity; + int testmode; + int withoutprompt; + + char *dev_name; + int devno; + char *dev_filename,*devno_param_str,*range_param_str; + char *start_param_str,*end_param_str,*blksize_param_str; + + format_data_t format_params; + + int rc; + int oc; + char *endptr; + + char c1,c2,cbuffer[6]; /* should be able to contain -end plus 1 char */ + int i1,i2; + char *str; + + int start_specified,end_specified,blksize_specified; + int devfile_specified,devno_specified,range_specified; + + /******************* initialization ********************/ + + endptr=NULL; + + /* set default values */ + format_params.start_unit=0; + format_params.stop_unit=-1; + format_params.blksize=4096; + testmode=0; + verbosity=0; + withoutprompt=0; + start_specified=end_specified=blksize_specified=0; + devfile_specified=devno_specified=range_specified=0; + + /*************** parse parameters **********************/ + + /* avoid error message generated by getopt */ + opterr=0; + + while ( (oc=getopt(argc,argv,"r:s:e:b:n:f:hty?vV")) !=EOF) { + switch (oc) { + case 'y': + withoutprompt=1; + break; + + case 't': + testmode=1; + break; + + case 'v': + verbosity++; + break; + + case '?': /* fall-through */ + case ':': + exit_usage(EXIT_MISUSE); + + case 'h': + exit_usage(0); + + case 'V': + printf("%s version 0.99\n",prog_name); + exit(0); + + case 's' : + start_param_str=optarg; + start_specified++; + break; + + case 'e' : + end_param_str=optarg; + end_specified++; + break; + + case 'b' : + blksize_param_str=optarg; + blksize_specified++; + break; + + case 'n' : + devno_param_str=optarg; + devno_specified++; + break; + + case 'f' : + dev_filename=optarg; + devfile_specified++; + break; + case 'r' : + range_param_str=optarg; + range_specified++; + break; + } + } + + /******************** checking of parameters **************/ + + /* convert range into -s and -e */ + CHECK_SPEC_MAX_ONCE(range_specified,"formatting range"); + + while (range_specified) { + start_specified++; + end_specified++; + + /* scan for 1 or 2 integers, separated by a dash */ + rc=sscanf(range_param_str,"%d%c%d%c",&i1,&c1,&i2,&c2); + if ((rc==3)&&(c1=='-')) { + format_params.start_unit=i1; + format_params.stop_unit=i2; + break; + } + if (rc==1) { + format_params.start_unit=i1; + break; + } + + /* scan for integer and -END */ + rc=sscanf(range_param_str,"%d%s",&i1,cbuffer); + if ((rc==2)&&(!strcasecmp(cbuffer,"-END"))) { + format_params.start_unit=i1; + format_params.stop_unit=-1; + break; + } + ERRMSG_EXIT(EXIT_MISUSE,"%s: specified range " \ + "is in invalid format\n",prog_name); + } + + if ((!devfile_specified)&&(!devno_specified)) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "not specified\n",prog_name); + + if ((devfile_specified+devno_specified)>1) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "can only be specified once\n",prog_name); + + if ((!start_specified)&&(!end_specified)&&(!range_specified)&& + (!blksize_specified)) { + format_params=ask_user_for_data(format_params); + } + + CHECK_SPEC_MAX_ONCE(start_specified,"start track"); + CHECK_SPEC_MAX_ONCE(end_specified,"end track"); + CHECK_SPEC_MAX_ONCE(blksize_specified,"blocksize"); + + if (devno_specified) + PARSE_PARAM_INTO(devno,devno_param_str,16,"device number"); + if (start_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.start_unit,start_param_str,10, + "start track"); + if (end_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.stop_unit,end_param_str,10, + "end track"); + if (blksize_specified) + PARSE_PARAM_INTO(format_params.blksize,blksize_param_str,10, + "blocksize"); + + /***********get dev_name *********************/ + dev_name=(devno_specified)? + get_devname_from_devno(devno,verbosity): + dev_filename; + + /*** range checking *********/ + str=check_param(CHECK_ALL,format_params); + if (str!=NULL) ERRMSG_EXIT(EXIT_MISUSE,"%s: %s\n",prog_name,str); + + /*************** issue the real command *****************/ + do_format_dasd(dev_name,format_params,testmode,verbosity, + withoutprompt); + + /*************** cleanup ********************************/ + if (strncmp(dev_name,TEMPFILENAME,TEMPFILENAMECHARS)==0) { + rc=unlink(dev_name); + if ((rc)&&(verbosity>=1)) + ERRMSG("%s: temporary device node %s could not be " \ + "removed: %s\n",prog_name,dev_name, + strerror(errno)); + } else { + if (devno_specified) { + /* so we have allocated space for the filename */ + free(dev_name); + } + } + + return 0; +} diff --git a/arch/s390/tools/silo/Makefile b/arch/s390/tools/silo/Makefile new file mode 100644 index 000000000000..bb043cc6b781 --- /dev/null +++ b/arch/s390/tools/silo/Makefile @@ -0,0 +1,8 @@ +all: silo + +silo: silo.o cfg.o + $(CROSS_COMPILE)gcc -o $@ $^ + +clean: + rm -f *.o silo + diff --git a/arch/s390/tools/silo/cfg.c b/arch/s390/tools/silo/cfg.c new file mode 100644 index 000000000000..2b11d93b8f83 --- /dev/null +++ b/arch/s390/tools/silo/cfg.c @@ -0,0 +1,373 @@ +/* cfg.c - Configuration file parser */ + +/* Copyright 1992-1997 Werner Almesberger. See file COPYING for details. */ + + +#include +#include +#include +#include +#include + +#include "cfg.h" + +#define MAX_TOKEN 200 +#define MAX_VAR_NAME MAX_TOKEN + +static FILE *file; +static char flag_set; +static char *last_token = NULL,*last_item = NULL,*last_value = NULL; +static int line_num; +static char *file_name = NULL; +static int back = 0; /* can go back by one char */ + + +void pdie(char *msg) +{ + fflush(stdout); + perror(msg); + exit(1); +} + + +void die(char *fmt,...) +{ + va_list ap; + + fflush(stdout); + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + fputc('\n',stderr); + exit(1); +} + +char *pstrdup(const char *str) +{ + char *this; + + if ((this = strdup(str)) == NULL) pdie("Out of memory"); + return this; +} + +int cfg_open(char *name) +{ + if (!strcmp(name,"-")) file = stdin; + else if (!(file = fopen(file_name = name,"r"))) pdie(name); + line_num = 1; + return fileno(file); +} + +void cfg_error(char *msg,...) +{ + va_list ap; + + fflush(stdout); + va_start(ap,msg); + vfprintf(stderr,msg,ap); + va_end(ap); + if (!file_name) fputc('\n',stderr); + else fprintf(stderr," near line %d in file %s\n",line_num,file_name); + exit(1); +} + + +static int next_raw(void) +{ + int ch; + + if (!back) return getc(file); + ch = back; + back = 0; + return ch; +} + + +static int next(void) +{ + static char *var; + char buffer[MAX_VAR_NAME+1]; + int ch,braced; + char *put; + + if (back) { + ch = back; + back = 0; + return ch; + } + if (var && *var) return *var++; + ch = getc(file); + if (ch == '\\') { + ch = getc(file); + if (ch == '$') return ch; + ungetc(ch,file); + return '\\'; + } + if (ch != '$') return ch; + ch = getc(file); + braced = ch == '{'; + put = buffer; + if (!braced) *put++ = ch; + while (1) { + ch = getc(file); +#if 0 + if (!braced && ch < ' ') { + ungetc(ch,file); + break; + } +#endif + if (ch == EOF) cfg_error("EOF in variable name"); + if (ch < ' ') cfg_error("control character in variable name"); + if (braced && ch == '}') break; + if (!braced && !isalpha(ch) && !isdigit(ch) && ch != '_') { + ungetc(ch,file); + break; + } + if (put-buffer == MAX_VAR_NAME) cfg_error("variable name too long"); + *put++ = ch; + } + *put = 0; + if (!(var = getenv(buffer))) cfg_error("unknown variable \"%s\"",buffer); + return next(); +} + + +static void again(int ch) +{ + if (back) die("internal error: again invoked twice"); + back = ch; +} + + +static char *cfg_get_token(void) +{ + char buf[MAX_TOKEN+1]; + char *here; + int ch,escaped; + + if (last_token) { + here = last_token; + last_token = NULL; + return here; + } + while (1) { + while ((ch = next()), ch == ' ' || ch == '\t' || ch == '\n') + if (ch == '\n') line_num++; + if (ch == EOF) return NULL; + if (ch != '#') break; + while ((ch = next_raw()), ch != '\n') + if (ch == EOF) return NULL; + line_num++; + } + if (ch == '=') return pstrdup("="); + if (ch == '"') { + here = buf; + while (here-buf < MAX_TOKEN) { + if ((ch = next()) == EOF) cfg_error("EOF in quoted string"); + if (ch == '"') { + *here = 0; + return pstrdup(buf); + } + if (ch == '\\') { + ch = next(); + if (ch != '"' && ch != '\\' && ch != '\n') + cfg_error("Bad use of \\ in quoted string"); + if (ch == '\n') { + while ((ch = next()), ch == ' ' || ch == '\t'); + if (!ch) continue; + again(ch); + ch = ' '; + } + } + if (ch == '\n' || ch == '\t') + cfg_error("\\n and \\t are not allowed in quoted strings"); + *here++ = ch; + } + cfg_error("Quoted string is too long"); + return 0; /* not reached */ + } + here = buf; + escaped = 0; + while (here-buf < MAX_TOKEN) { + if (escaped) { + if (ch == EOF) cfg_error("\\ precedes EOF"); + if (ch == '\n') line_num++; + else *here++ = ch == '\t' ? ' ' : ch; + escaped = 0; + } + else { + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' || + ch == '=' || ch == EOF) { + again(ch); + *here = 0; + return pstrdup(buf); + } + if (!(escaped = (ch == '\\'))) *here++ = ch; + } + ch = next(); + } + cfg_error("Token is too long"); + return 0; /* not reached */ +} + + +static void cfg_return_token(char *token) +{ + last_token = token; +} + + +static int cfg_next(char **item,char **value) +{ + char *this; + + if (last_item) { + *item = last_item; + *value = last_value; + last_item = NULL; + return 1; + } + *value = NULL; + if (!(*item = cfg_get_token())) return 0; + if (!strcmp(*item,"=")) cfg_error("Syntax error"); + if (!(this = cfg_get_token())) return 1; + if (strcmp(this,"=")) { + cfg_return_token(this); + return 1; + } + if (!(*value = cfg_get_token())) cfg_error("Value expected at EOF"); + if (!strcmp(*value,"=")) cfg_error("Syntax error after %s",*item); + return 1; +} + + +static void cfg_return(char *item,char *value) +{ + last_item = item; + last_value = value; +} + + +void cfg_init(CONFIG *table) +{ + while (table->type != cft_end) { + switch (table->type) { + case cft_strg: + if (table->data) free(table->data); + case cft_flag: + table->data = NULL; + break; + case cft_link: + table = ((CONFIG *) table->action)-1; + break; + default: + die("Unknown syntax code %d",table->type); + } + table++; + } +} + + +static int cfg_do_set(CONFIG *table,char *item,char *value,int copy, + void *context) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (value && walk->type != cft_strg) + cfg_error("'%s' doesn't have a value",walk->name); + if (!value && walk->type == cft_strg) + cfg_error("Value expected for '%s'",walk->name); + if (walk->data) { + if (walk->context == context) + cfg_error("Duplicate entry '%s'",walk->name); + else { + fprintf(stderr,"Ignoring entry '%s'\n",walk->name); + if (!copy) free(value); + return 1; + } + } + if (walk->type == cft_flag) walk->data = &flag_set; + else if (walk->type == cft_strg) { + if (copy) walk->data = pstrdup(value); + else walk->data = value; + } + walk->context = context; + if (walk->action) ((void (*)(void)) walk->action)(); + break; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + if (walk->type != cft_end) return 1; + cfg_return(item,value); + return 0; +} + + +void cfg_set(CONFIG *table,char *item,char *value,void *context) +{ + if (!cfg_do_set(table,item,value,1,context)) + cfg_error("cfg_set: Can't set %s",item); +} + + +void cfg_unset(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) + if (walk->name && !strcasecmp(walk->name,item)) { + if (!walk->data) die("internal error (cfg_unset %s, unset)",item); + if (walk->type == cft_strg) free(walk->data); + walk->data = NULL; + return; + } + die("internal error (cfg_unset %s, unknown",item); +} + + +int cfg_parse(CONFIG *table) +{ + char *item,*value; + + while (1) { + if (!cfg_next(&item,&value)) return 0; + if (!cfg_do_set(table,item,value,0,table)) return 1; + free(item); + } +} + + +int cfg_get_flag(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (walk->type != cft_flag) + die("cfg_get_flag: operating on non-flag %s",item); + return !!walk->data; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + die("cfg_get_flag: unknown item %s",item); + return 0; /* not reached */ +} + + +char *cfg_get_strg(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (walk->type != cft_strg) + die("cfg_get_strg: operating on non-string %s",item); + return walk->data; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + die("cfg_get_strg: unknown item %s",item); + return 0; /* not reached */ +} diff --git a/arch/s390/tools/silo/cfg.h b/arch/s390/tools/silo/cfg.h new file mode 100644 index 000000000000..7022fdd21467 --- /dev/null +++ b/arch/s390/tools/silo/cfg.h @@ -0,0 +1,58 @@ +/* cfg.h - Configuration file parser */ + +/* Copyright 1992-1996 Werner Almesberger. See file COPYING for details. */ + + +#ifndef CFG_H +#define CFG_H + +typedef enum { cft_strg, cft_flag, cft_link, cft_end } CONFIG_TYPE; + +typedef struct { + CONFIG_TYPE type; + char *name; + void *action; + void *data; + void *context; +} CONFIG; + +extern int cfg_open(char *name); + +/* Opens the configuration file. Returns the file descriptor of the open + file. */ + +extern void cfg_error(char *msg,...); + +/* Signals an error while parsing the configuration file and terminates the + program. */ + +extern void cfg_init(CONFIG *table); + +/* Initializes the specified table. */ + +extern void cfg_set(CONFIG *table,char *item,char *value,void *context); + +/* Sets the specified variable in table. If the variable has already been set + since the last call to cfg_init, a warning message is issued if the context + keys don't match or a fatal error is reported if they do. */ + +extern void cfg_unset(CONFIG *table,char *item); + +/* Unsets the specified variable in table. It is a fatal error if the variable + was not set. */ + +extern int cfg_parse(CONFIG *table); + +/* Parses the configuration file for variables contained in table. A non-zero + value is returned if a variable not found in table has been met. Zero is + returned if EOF has been reached. */ + +extern int cfg_get_flag(CONFIG *table,char *item); + +/* Returns one if the specified variable is set, zero if it isn't. */ + +extern char *cfg_get_strg(CONFIG *table,char *item); + +/* Returns the value of the specified variable if it is set, NULL otherwise. */ + +#endif diff --git a/arch/s390/tools/silo/silo.c b/arch/s390/tools/silo/silo.c new file mode 100644 index 000000000000..a4fdd40b8358 --- /dev/null +++ b/arch/s390/tools/silo/silo.c @@ -0,0 +1,509 @@ +/* + * arch/s390/boot/silo.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski + * + * Fritz Elfert contributed support for + * /etc/silo.conf based on Intel's lilo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" + +CONFIG cf_options[] = { + { cft_strg, "append", NULL, NULL,NULL }, + { cft_strg, "image", NULL, NULL,NULL }, + { cft_strg, "ipldevice", NULL, NULL,NULL }, + { cft_strg, "bootsect", NULL, NULL,NULL }, + { cft_strg, "map", NULL, NULL,NULL }, + { cft_strg, "parmfile", NULL, NULL,NULL }, + { cft_strg, "ramdisk", NULL, NULL,NULL }, + { cft_strg, "root", NULL, NULL,NULL }, + { cft_flag, "readonly", NULL, NULL,NULL }, + { cft_strg, "verbose", NULL, NULL,NULL }, + { cft_end, NULL, NULL, NULL,NULL } +}; + +/* from dasd.h */ +#define DASD_PARTN_BITS 2 +#define BIODASDRWTB _IOWR('D',5,int) +/* end */ + +#define SILO_CFG "/etc/silo.conf" + +#define PRINT_LEVEL(x,y...) if ( silo_options.verbosity >= x ) printf(y) +#define ERROR_LEVEL(x,y...) if ( silo_options.verbosity >= x ) fprintf(stderr,y) +#define TOGGLE(x) ((x)=((x)?(0):(1))) +#define GETARG(x) {int len=strlen(optarg);x=malloc(len);strncpy(x,optarg,len);PRINT_LEVEL(1,"%s set to %s\n",#x,optarg);} + +#define ITRY(x) if ( (x) == -1 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } +#define NTRY(x) if ( (x) == 0 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } + +#define MAX_CLUSTERS 256 +#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) + +#define SILO_VERSION "1.1" + +struct silo_options + { + short int verbosity; + struct + { + unsigned char testonly; + } + flags; + char *image; + char *ipldevice; + char *parmfile; + char *ramdisk; + char *bootsect; + char *conffile; + } +silo_options = +{ + 1, /* verbosity */ + { + 0, /* testonly */ + } + , + "./image", /* image */ + NULL, /* ipldevice */ + NULL, /* parmfile */ + NULL, /* initrd */ + "ipleckd.boot", /* bootsector */ + SILO_CFG /* silo.conf file */ +}; + +struct blockdesc + { + unsigned long off; + unsigned short ct; + unsigned long addr; + }; + +struct blocklist + { + struct blockdesc blk[MAX_CLUSTERS]; + unsigned short ix; + }; + +void +usage (void) +{ + printf ("Usage:\n"); + printf ("silo -d ipldevice [additional options]\n"); + printf ("-d /dev/node : set ipldevice to /dev/node\n"); + printf ("-f image : set image to image\n"); + printf ("-F conffile : specify configuration file (/etc/silo.conf)\n"); + printf ("-p parmfile : set parameter file to parmfile\n"); + printf ("-b bootsect : set bootsector to bootsect\n"); + printf ("Additional options\n"); + printf ("-v: increase verbosity level\n"); + printf ("-v#: set verbosity level to #\n"); + printf ("-t: toggle testonly flag\n"); + printf ("-h: print this message\n"); + printf ("-?: print this message\n"); + printf ("-V: print version\n"); +} + +int +read_cfg(struct silo_options *o) +{ + if (access(o->conffile, R_OK) && (errno == ENOENT)) + return 0; + /* If errno != ENOENT, let cfg_open report an error */ + cfg_open(o->conffile); + cfg_parse(cf_options); + o->ipldevice = cfg_get_strg(cf_options, "ipldevice"); + o->image = cfg_get_strg(cf_options, "image"); + o->parmfile = cfg_get_strg(cf_options, "parmfile"); + o->ramdisk = cfg_get_strg(cf_options, "ramdisk"); + o->bootsect = cfg_get_strg(cf_options, "bootsect"); + if (cfg_get_strg(cf_options, "verbose")) { + unsigned short v; + sscanf (cfg_get_strg(cf_options, "verbose"), "%hu", &v); + o->verbosity = v; + } + return 1; +} + +char * +gen_tmpparm() +{ + char *append = cfg_get_strg(cf_options, "append"); + char *root = cfg_get_strg(cf_options, "root"); + int ro = cfg_get_flag(cf_options, "readonly"); + FILE *f; + char *fn; + char *tmpdir=NULL,*save=NULL; + + if (!append && !root && !ro) + return NULL; + + tmpdir=getenv("TMPDIR"); + if (tmpdir) { + NTRY( save=(char*)malloc(strlen(tmpdir))); + NTRY( strcpy(save,tmpdir)); + } + ITRY( setenv("TMPDIR",".",1)); + NTRY( fn = tempnam(NULL,"parm.")); + NTRY( f = fopen(fn, "w")); + if (root) + fprintf(f, "root=%s ", root); + if (ro) + fprintf(f, "ro "); + if (append) + fprintf(f, "%s", append); + fprintf(f, "\n"); + fclose(f); + if ( save ) + ITRY( setenv("TMPDIR",save,1)); + return strdup(fn); +} + +int +parse_options (struct silo_options *o, int argc, char *argv[]) +{ + int rc = 0; + int oc; + + read_cfg(o); + while ((oc = getopt (argc, argv, "Vf:F:d:p:r:b:B:h?v::t")) != -1) + { + switch (oc) + { + case 'V': + printf("silo version: %s\n",SILO_VERSION); + exit(0); + case 't': + TOGGLE (o->flags.testonly); + PRINT_LEVEL (1, "Testonly flag is now %sactive\n", o->flags.testonly ? "" : "in"); + break; + case 'v': + { + unsigned short v; + if (optarg && sscanf (optarg, "%hu", &v)) + o->verbosity = v; + else + o->verbosity++; + PRINT_LEVEL (1, "Verbosity value is now %hu\n", o->verbosity); + break; + } + case 'h': + case '?': + usage (); + exit(0); + case 'd': + GETARG (o->ipldevice); + break; + case 'f': + GETARG (o->image); + break; + case 'F': + GETARG (o->conffile); + break; + case 'p': + GETARG (o->parmfile); + break; + case 'r': + GETARG (o->ramdisk); + break; + case 'b': + GETARG (o->bootsect); + break; + default: + rc = EINVAL; + break; + } + } + + return rc; +} + +int +verify_device (char *name) +{ + int rc = 0; + struct stat dst; + struct stat st; + ITRY (stat (name, &dst)); + if (S_ISBLK (dst.st_mode)) + { + if (!(MINOR (dst.st_rdev) & PARTN_MASK)) + { + rc = dst.st_rdev; + } + else + /* invalid MINOR & PARTN_MASK */ + { + ERROR_LEVEL (1, "Cannot boot from partition %d %d %d", + (int) PARTN_MASK, (int) MINOR (dst.st_rdev), (int) (PARTN_MASK & MINOR (dst.st_rdev))); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISBLK */ + { + ERROR_LEVEL (1, "%s is no block device\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_file (char *name, int dev) +{ + int rc = 0; + struct stat dst; + struct stat st; + int bs = 1024; + int l; + ITRY (stat (name, &dst)); + if (S_ISREG (dst.st_mode)) + { + if ((unsigned) MAJOR (dev) == (unsigned) MAJOR (dst.st_dev) && (unsigned) MINOR (dev) == (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)) + { + /* whatever to do if all is ok... */ + } + else + /* devicenumber doesn't match */ + { + ERROR_LEVEL (1, "%s is not on device (%d/%d) but on (%d/%d)\n", name, (unsigned) MAJOR (dev), (unsigned) MINOR (dev), (unsigned) MAJOR (dst.st_dev), (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISREG */ + { + ERROR_LEVEL (1, "%s is neither regular file nor linkto one\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_options (struct silo_options *o) +{ + int rc = 0; + int dev = 0; + int crc = 0; + if (!o->ipldevice || !o->image || !o->bootsect) + { + usage (); + exit (1); + } + PRINT_LEVEL (1, "IPL device is: '%s'", o->ipldevice); + + ITRY (dev = verify_device (o->ipldevice)); + PRINT_LEVEL (2, "...ok...(%d/%d)", (unsigned short) MAJOR (dev), (unsigned short) MINOR (dev)); + PRINT_LEVEL (1, "\n"); + + PRINT_LEVEL (0, "bootsector is: '%s'", o->bootsect); + ITRY (verify_file (o->bootsect, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + PRINT_LEVEL (0, "Kernel image is: '%s'", o->image); + ITRY (verify_file (o->image, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + if (o->parmfile) + { + PRINT_LEVEL (0, "parameterfile is: '%s'", o->parmfile); + ITRY (verify_file (o->parmfile, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + } + + if (o->ramdisk) + { + PRINT_LEVEL (0, "initialramdisk is: '%s'", o->ramdisk); + ITRY (verify_file (o->ramdisk, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + } + + return crc; +} + + +int +add_file_to_blocklist (char *name, struct blocklist *lst, long addr) +{ + int fd; + int devfd; + struct stat fst; + int i; + int blk; + int bs; + int blocks; + + int rc = 0; + + ITRY (fd = open (name, O_RDONLY)); + ITRY (fstat (fd, &fst)); + ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, fst.st_dev)); + ITRY (devfd = open ("/tmp/silodev", O_RDONLY)); + ITRY (ioctl (fd, FIGETBSZ, &bs)); + blocks = (fst.st_size + bs - 1) / bs; + for (i = 0; i < blocks; i++) + { + blk = i; + ITRY (ioctl (fd, FIBMAP, &blk)); + if (blk) + { + int oldblk = blk; + ITRY (ioctl (devfd, BIODASDRWTB, &blk)); + if (blk <= 0) + { + ERROR_LEVEL (0, "BIODASDRWTB on blk %d returned %d\n", oldblk, blk); + break; + } + } + else + { + PRINT_LEVEL (1, "Filled hole on blk %d\n", i); + } + if (lst->ix == 0 || i == 0 || + lst->blk[lst->ix - 1].ct >= 128 || + (lst->blk[lst->ix - 1].off + lst->blk[lst->ix - 1].ct != blk && + !(lst->blk[lst->ix - 1].off == 0 && blk == 0))) + { + if (lst->ix >= MAX_CLUSTERS) + { + rc = 1; + errno = ENOMEM; + break; + } + lst->blk[lst->ix].off = blk; + lst->blk[lst->ix].ct = 1; + lst->blk[lst->ix].addr = addr + i * bs; + lst->ix++; + } + else + { + lst->blk[lst->ix - 1].ct++; + } + } + ITRY(unlink("/tmp/silodev")); + return rc; +} + +int +write_bootsect (char *ipldevice, char *bootsect, struct blocklist *blklst) +{ + int i; + int s_fd, d_fd, b_fd, bd_fd; + struct stat s_st, d_st, b_st; + int rc=0; + int bs, boots; + char *mapname; + char *tmpdev; + char buffer[4096]={0,}; + ITRY (d_fd = open (ipldevice, O_RDWR | O_SYNC)); + ITRY (fstat (d_fd, &d_st)); + if (!(mapname = cfg_get_strg(cf_options, "map"))) + mapname = "boot.map"; + ITRY (s_fd = open (mapname, O_RDWR | O_TRUNC | O_CREAT | O_SYNC)); + ITRY (verify_file (bootsect, d_st.st_rdev)); + for (i = 0; i < blklst->ix; i++) + { + int offset = blklst->blk[i].off; + int addrct = blklst->blk[i].addr | (blklst->blk[i].ct & 0xff); + PRINT_LEVEL (1, "ix %i: offset: %06x count: %02x address: 0x%08x\n", i, offset, blklst->blk[i].ct & 0xff, blklst->blk[i].addr); + NTRY (write (s_fd, &offset, sizeof (int))); + NTRY (write (s_fd, &addrct, sizeof (int))); + } + ITRY (ioctl (s_fd,FIGETBSZ, &bs)); + ITRY (stat (mapname, &s_st)); + if (s_st.st_size > bs ) + { + ERROR_LEVEL (0,"%s is larger than one block\n", mapname); + rc = -1; + errno = EINVAL; + } + boots=0; + NTRY ( tmpdev = tmpnam(NULL) ); + ITRY (mknod (tmpdev, S_IFBLK | S_IRUSR | S_IWUSR, s_st.st_dev)); + ITRY (bd_fd = open (tmpdev, O_RDONLY)); + ITRY ( ioctl(s_fd,FIBMAP,&boots)); + ITRY (ioctl (bd_fd, BIODASDRWTB, &boots)); + PRINT_LEVEL (1, "Bootmap is in block no: 0x%08x\n", boots); + close (bd_fd); + close(s_fd); + ITRY (unlink(tmpdev)); + /* Now patch the bootsector */ + ITRY (b_fd = open (bootsect, O_RDONLY)); + NTRY (read (b_fd, buffer, 4096)); + memset (buffer + 0xe0, 0, 8); + *(int *) (buffer + 0xe0) = boots; + if ( ! silo_options.flags.testonly ) { + NTRY (write (d_fd, buffer, 4096)); + NTRY (write (d_fd, buffer, 4096)); + } + close (b_fd); + close (d_fd); + return rc; +} + +int +do_silo (struct silo_options *o) +{ + int rc = 0; + char *tmp_parmfile = NULL; + + int device_fd; + int image_fd; + struct blocklist blklist; + memset (&blklist, 0, sizeof (struct blocklist)); + ITRY (add_file_to_blocklist (o->image, &blklist, 0x00000000)); + if (o->parmfile) + { + ITRY (add_file_to_blocklist (o->parmfile, &blklist, 0x00008000)); + } + else + { + if ((tmp_parmfile = gen_tmpparm())) + ITRY (add_file_to_blocklist (tmp_parmfile, &blklist, 0x00008000)); + } + if (o->ramdisk) + { + ITRY (add_file_to_blocklist (o->ramdisk, &blklist, 0x00800000)); + } + ITRY (write_bootsect (o->ipldevice, o->bootsect, &blklist)); + if (tmp_parmfile) + ITRY (remove (tmp_parmfile)); + + return rc; +} + +int +main (int argct, char *argv[]) +{ + int rc = 0; + ITRY (parse_options (&silo_options, argct, argv)); + ITRY (verify_options (&silo_options)); + ITRY (do_silo (&silo_options)); + return rc; +} diff --git a/arch/s390/tools/silo/silo.conf b/arch/s390/tools/silo/silo.conf new file mode 100644 index 000000000000..216e2b47f148 --- /dev/null +++ b/arch/s390/tools/silo/silo.conf @@ -0,0 +1,7 @@ +ipldevice = /dev/dasd00 +image = /boot/image +bootsect = /boot/ipleckd.boot +map = /boot/boot.map +root = /dev/dasd01 +readonly +append = "dasd=200-20f" diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index 26f3194bd266..285de0e9f7c0 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -713,7 +713,7 @@ int probe_irq_off(unsigned long mask) * */ -__initfunc(void init_IRQ(void)) +unsigned long __init init_IRQ(unsigned long memory) { extern void sun4c_init_IRQ( void ); extern void sun4m_init_IRQ( void ); @@ -751,4 +751,5 @@ __initfunc(void init_IRQ(void)) break; } btfixup(); + return memory; } diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index dafefbb7e305..b88ec687ae30 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -241,6 +241,7 @@ if [ "$CONFIG_NET" = "y" ]; then tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 tristate 'PCI NE2000 support' CONFIG_NE2K_PCI tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 + tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN fi # bool 'FDDI driver support' CONFIG_FDDI # if [ "$CONFIG_FDDI" = "y" ]; then diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 5854f646d96e..5ea3aff0945a 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1399,7 +1399,7 @@ void enable_prom_timer(void) prom_timers->count0 = 0; } -__initfunc(void init_IRQ(void)) +unsigned long __init init_IRQ(unsigned long memory) { static int called = 0; @@ -1430,4 +1430,5 @@ __initfunc(void init_IRQ(void)) : /* No outputs */ : "i" (PSTATE_IE) : "g1"); + return memory; } diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index ad9625f582e5..27caea3a2276 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -159,7 +159,7 @@ __initfunc(void smp_callin(void)) } extern int cpu_idle(void *unused); -extern void init_IRQ(void); +extern unsigned long init_IRQ(unsigned long); void initialize_secondary(void) { @@ -168,7 +168,8 @@ void initialize_secondary(void) int start_secondary(void *unused) { trap_init(); - init_IRQ(); + /* No memory allocation allowed on slave IRQ init */ + init_IRQ(NULL); smp_callin(); return cpu_idle(NULL); } diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index abd3bd223091..0376b20615b0 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -799,9 +799,6 @@ static void idedisk_setup (ide_drive_t *drive) /* Use physical geometry if what we have still makes no sense */ if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) { - if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) { - id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors))); - } drive->cyl = id->cyls; drive->head = id->heads; drive->sect = id->sectors; diff --git a/drivers/char/console.c b/drivers/char/console.c index 57224683cdcb..6555629bb084 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -91,9 +91,6 @@ #include #include #include -#ifdef CONFIG_APM -#include -#endif #include #include @@ -187,6 +184,12 @@ DECLARE_TASK_QUEUE(con_task_queue); */ static int scrollback_delta = 0; +/* + * Hook so that the power management routines can (un)blank + * the console on our behalf. + */ +int (*console_blank_hook)(int) = NULL; + /* * Low-Level Functions */ @@ -2539,10 +2542,8 @@ void do_blank_screen(int entering_gfx) if (i) set_origin(currcons); -#ifdef CONFIG_APM - if (apm_display_blank()) + if (console_blank_hook && console_blank_hook(1)) return; -#endif if (vesa_blank_mode) sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } @@ -2566,9 +2567,8 @@ void unblank_screen(void) currcons = fg_console; console_blanked = 0; -#ifdef CONFIG_APM - apm_display_unblank(); -#endif + if (console_blank_hook) + console_blank_hook(0); if (sw->con_blank(vc_cons[currcons].d, 0)) /* Low-level driver cannot restore -> do it ourselves */ update_screen(fg_console); diff --git a/drivers/char/hfmodem/tables.h b/drivers/char/hfmodem/tables.h new file mode 100644 index 000000000000..d976c8e53bce --- /dev/null +++ b/drivers/char/hfmodem/tables.h @@ -0,0 +1,90 @@ +/* + * This file is automatically generated by ./gentbl, DO NOT EDIT! +*/ + +#define SINTABBITS 9 +#define SINTABSIZE (1< + * who actually finally proved there really was a race. */ #include @@ -38,6 +42,7 @@ #include #include #include +#include #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1) @@ -57,13 +62,22 @@ #define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ #define TTY_THRESHOLD_UNTHROTTLE 128 +static spinlock_t __attribute((unused)) tty_put_lock = SPIN_LOCK_UNLOCKED; + static inline void put_tty_queue(unsigned char c, struct tty_struct *tty) { + unsigned long flags; + /* + * The problem of stomping on the buffers ends here. + * Why didn't anyone see this one comming? --AJK + */ + spin_lock_irqsave(&tty_put_lock, flags); if (tty->read_cnt < N_TTY_BUF_SIZE) { tty->read_buf[tty->read_head] = c; tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1); tty->read_cnt++; } + spin_unlock_irqrestore(&tty_put_lock, flags); } /* diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index b266d44505e9..fe00329ab7aa 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -24,10 +24,6 @@ #include -#ifdef CONFIG_APM -#include -#endif - #ifdef CONFIG_MAGIC_SYSRQ int sysrq_enabled = 1; #endif @@ -37,6 +33,9 @@ extern void reset_vc(unsigned int); extern int console_loglevel; extern struct vfsmount *vfsmntlist; +/* Machine specific power off function */ +void (*sysrq_power_off)(void) = NULL; + /* Send a signal to all user processes */ static void send_sig_all(int sig, int even_init) @@ -86,12 +85,12 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, printk("Resetting\n"); machine_restart(NULL); break; -#ifdef CONFIG_APM case 'o': /* O -- power off */ - printk("Power off\n"); - apm_power_off(); + if (sysrq_power_off) { + printk("Power off\n"); + sysrq_power_off(); + } break; -#endif case 's': /* S -- emergency sync */ printk("Emergency Sync\n"); emergency_sync_scheduled = EMERG_SYNC; @@ -141,11 +140,10 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, if (tty) printk("saK "); #endif - printk("Boot " -#ifdef CONFIG_APM - "Off " -#endif - "Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n"); + printk("Boot "); + if (sysrq_power_off) + printk("Off "); + printk("Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n"); /* Don't use 'A' as it's handled specially on the Sparc */ } diff --git a/drivers/isdn/avmb1/t1pci.c b/drivers/isdn/avmb1/t1pci.c index 6fe0a8727a66..d73e93c0a0bd 100644 --- a/drivers/isdn/avmb1/t1pci.c +++ b/drivers/isdn/avmb1/t1pci.c @@ -55,7 +55,7 @@ static char *revision = "$Revision: 1.3 $"; /* ------------------------------------------------------------- */ -int suppress_pollack = 0; +static int suppress_pollack = 0; MODULE_AUTHOR("Carsten Paeth "); diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 615826451368..43ca69d1d799 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -43,7 +43,12 @@ static int rxdmacount = 0; /* Set the copy breakpoint for the copy-only-tiny-buffer Rx method. Lower values use more memory, but are faster. */ +#ifdef __alpha__ +/* force copying of all packets to avoid unaligned accesses on Alpha */ +static int rx_copybreak = 1518; +#else static int rx_copybreak = 200; +#endif /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; @@ -561,7 +566,7 @@ int eepro100_init(void) for (; pci_index < 8; pci_index++) { unsigned char pci_bus, pci_device_fn, pci_latency; - u32 pciaddr; + unsigned long pciaddr; long ioaddr; int irq; @@ -1736,7 +1741,7 @@ speedo_rx(struct net_device *dev) skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ -#if 1 || USE_IP_CSUM +#if !defined(__alpha__) /* Packet is in one chunk -- we can copy + cksum. */ eth_copy_and_sum(skb, sp->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/net/eql.c b/drivers/net/eql.c index 2af3d4523019..12f8e1dd0ff3 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -1030,6 +1030,8 @@ int init_module(void) void cleanup_module(void) { + kfree(((equalizer_t *)dev_eql.priv)->stats ); + kfree(dev_eql.priv); unregister_netdev(&dev_eql); } #endif /* MODULE */ diff --git a/drivers/net/sk98lin/h/skdrv2nd.h b/drivers/net/sk98lin/h/skdrv2nd.h index c85ba69355c8..23bd20add14f 100644 --- a/drivers/net/sk98lin/h/skdrv2nd.h +++ b/drivers/net/sk98lin/h/skdrv2nd.h @@ -411,6 +411,7 @@ struct s_AC { int RxBufSize; /* length of receive buffers */ struct net_device_stats stats; /* linux 'netstat -i' statistics */ int Index; /* internal board index number */ + SK_BOOL JumboActivated; /* jumbo support ever activated */ /* adapter RAM sizes for queues of active port */ int RxQueueSize; /* memory used for receive queue */ diff --git a/drivers/net/sk98lin/skcsum.c b/drivers/net/sk98lin/skcsum.c index 59c71ab491d7..a29d9cf8ad74 100644 --- a/drivers/net/sk98lin/skcsum.c +++ b/drivers/net/sk98lin/skcsum.c @@ -140,9 +140,11 @@ static const char SysKonnectFileId[] = "@(#)" */ #ifdef SK_LITTLE_ENDIAN #define SKCS_HTON16(Val16) (((unsigned) (Val16) >> 8) | (((Val16) & 0xFF) << 8)) +#define SKCS_INV_HTON16(Val16) (Val16) #endif /* SK_LITTLE_ENDIAN */ #ifdef SK_BIG_ENDIAN #define SKCS_HTON16(Val16) (Val16) +#define SKCS_INV_HTON16(Val16) (((unsigned) (Val16) >> 8) | (((Val16) & 0xFF) << 8)) #endif /* SK_BIG_ENDIAN */ #define SKCS_NTOH16(Val16) SKCS_HTON16(Val16) @@ -617,7 +619,6 @@ unsigned Checksum2) /* Hardware checksum 2. */ /* * Calculate the TCP/UDP checksum. */ - /* Get total length of IP header and data. */ IpDataLength = @@ -639,12 +640,11 @@ unsigned Checksum2) /* Hardware checksum 2. */ SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + - (unsigned long) (NextLevelProtocol << 8) + + (unsigned long) SKCS_HTON16(NextLevelProtocol) + (unsigned long) SKCS_HTON16(IpDataLength) + /* Add the TCP/UDP header checksum. */ - - (unsigned long) IpDataChecksum; + (unsigned long) SKCS_INV_HTON16(IpDataChecksum); /* Add-in any carries. */ diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c index 8f241aeff9d3..cf4d60728c1f 100644 --- a/drivers/net/sk98lin/skge.c +++ b/drivers/net/sk98lin/skge.c @@ -225,7 +225,7 @@ static const char SysKonnectFileId[] = "@(#)" __FILE__ " (C) SysKonnect."; static const char SysKonnectBuildNumber[] = - "@(#)SK-BUILD: 3.02 (19991111) PL: 01"; + "@(#)SK-BUILD: 3.04 (19991111) PL: 01"; #include @@ -234,10 +234,10 @@ static const char SysKonnectBuildNumber[] = /* defines ******************************************************************/ -#define BOOT_STRING "sk98lin: Network Device Driver v3.02\n" \ +#define BOOT_STRING "sk98lin: Network Device Driver v3.04\n" \ "Copyright (C) 1999 SysKonnect" -#define VER_STRING "3.02" +#define VER_STRING "3.04" /* for debuging on x86 only */ @@ -437,7 +437,12 @@ unsigned long base_address; */ +#ifndef __sparc_v9__ pAC->IoBase = (char*)ioremap(base_address, 0x4000); +#else + /* workaround for bug in 2.2 kernel for sparcv9 */ + pAC->IoBase = (char*)base_address; +#endif if (!pAC->IoBase){ printk(KERN_ERR "%s: Unable to map I/O register, " "SK 98xx No. %i will be disabled.\n", @@ -808,6 +813,14 @@ int Ret; /* return code of request_irq */ ProductStr(pAC); printk("%s: %s\n", dev->name, pAC->DeviceStr); + /* Print configuration settings */ + printk(" PrefPort:%c RlmtMode:%s\n", + 'A' + pAC->Rlmt.PrefPort, + (pAC->RlmtMode==0) ? "ChkLink" : + ((pAC->RlmtMode==1) ? "ChkLink" : + ((pAC->RlmtMode==3) ? "ChkOth" : + ((pAC->RlmtMode==7) ? "ChkSeg" : "Error")))); + SkGeYellowLED(pAC, pAC->IoBase, 1); /* @@ -1527,10 +1540,13 @@ int Rc; /* return code of XmitFrame */ Rc = XmitFrame(pAC, &pAC->TxPort[pAC->ActivePort][TX_PRIO_LOW], skb); - if (Rc == 0) { + if (Rc <= 0) { /* transmitter out of resources */ set_bit(0, (void*) &dev->tbusy); - return (0); + if (Rc == 0) + return (0); + else + return (-EBUSY); } dev->trans_start = jiffies; return (0); @@ -1555,9 +1571,9 @@ int Rc; /* return code of XmitFrame */ * * Returns: * > 0 - on succes: the number of bytes in the message - * = 0 - on resource shortage: this frame sent or dropped, now + * = 0 - on resource shortage: this frame was sent, now * the ring is full ( -> set tbusy) - * < 0 - on failure: other problems (not used) + * < 0 - on resource shortage: this frame could not be sent */ static int XmitFrame( SK_AC *pAC, /* pointer to adapter context */ @@ -1585,7 +1601,7 @@ int BytesSend; ("XmitFrame failed\n")); /* this message can not be sent now */ DEV_KFREE_SKB(pMessage); - return (0); + return (-1); } } /* advance head counter behind descriptor needed for this frame */ @@ -2414,6 +2430,7 @@ SK_EVPARA EvPara; * enable/disable hardware support for long frames */ if (NewMtu > 1500) { + pAC->JumboActivated = SK_TRUE; // is never set back !!! pAC->GIni.GIPortUsage = SK_JUMBO_LINK; for (i=0; iGIni.GIMacsFound; i++) { pAC->GIni.GP[i].PRxCmd = @@ -2523,7 +2540,13 @@ unsigned int Flags; /* for spin lock */ pAC->stats.tx_packets = (SK_U32) pPnmiStat->StatTxOkCts & 0xFFFFFFFF; pAC->stats.rx_bytes = (SK_U32) pPnmiStruct->RxOctetsDeliveredCts; pAC->stats.tx_bytes = (SK_U32) pPnmiStat->StatTxOctetsOkCts; - pAC->stats.rx_errors = (SK_U32) pPnmiStruct->InErrorsCts & 0xFFFFFFFF; + if (!pAC->JumboActivated) { + pAC->stats.rx_errors = (SK_U32) pPnmiStruct->InErrorsCts & 0xFFFFFFFF; + } + else { + pAC->stats.rx_errors = (SK_U32) ((pPnmiStruct->InErrorsCts - + pPnmiStat->StatRxTooLongCts) & 0xFFFFFFFF); + } pAC->stats.tx_errors = (SK_U32) pPnmiStat->StatTxSingleCollisionCts & 0xFFFFFFFF; pAC->stats.rx_dropped = (SK_U32) pPnmiStruct->RxNoBufCts & 0xFFFFFFFF; pAC->stats.tx_dropped = (SK_U32) pPnmiStruct->TxNoBufCts & 0xFFFFFFFF; diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index d76947188c9c..42dd9fd3cc75 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -1,4 +1,4 @@ -/******************************************************************** +/******************************************************************************** * * Linux ThunderLAN Driver * @@ -7,6 +7,7 @@ * * (C) 1997-1998 Caldera, Inc. * (C) 1998 James Banks + * (C) 1999-2000 Torben Mathiasen * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. @@ -34,7 +35,15 @@ * * Torben Mathiasen New Maintainer! * - ********************************************************************/ + * v1.2 Feb 14, 2000 - Fixed a timer bug that would cause + * the tlan driver to get stuck if no + * cable/link were present. + * - We now allow higher priority timers + * to overwrite timers like TLAN_TIMER_ACTIVITY + * Patch from John Cagle + * - Removed dependency of HZ being 100. + * + ********************************************************************************/ #include @@ -76,7 +85,7 @@ static int bbuf = 0; static u8 *TLanPadBuffer; static char TLanSignature[] = "TLAN"; static int TLanVersionMajor = 1; -static int TLanVersionMinor = 0; +static int TLanVersionMinor = 2; static TLanAdapterEntry TLanAdapterList[] = { @@ -238,7 +247,8 @@ TLan_SetTimer( struct device *dev, u32 ticks, u32 type ) TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; cli(); - if ( priv->timer.function != NULL ) { + if ( priv->timer.function != NULL && + priv->timerType != TLAN_TIMER_ACTIVITY) { return; } priv->timer.function = &TLan_Timer; @@ -712,6 +722,9 @@ int TLan_Open( struct device *dev ) int err; priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); + + MOD_INC_USE_COUNT; + if ( priv->sa_int ) { TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Using SA_INTERRUPT\n" ); err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev ); @@ -723,8 +736,6 @@ int TLan_Open( struct device *dev ) return -EAGAIN; } - MOD_INC_USE_COUNT; - dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; @@ -735,7 +746,6 @@ int TLan_Open( struct device *dev ) TLan_ResetLists( dev ); TLan_ReadAndClearStats( dev, TLAN_IGNORE ); TLan_ResetAdapter( dev ); - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Opened. TLAN Chip Rev: %x\n", dev->name, priv->tlanRev ); return 0; @@ -927,6 +937,7 @@ int TLan_Close(struct device *dev) outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); if ( priv->timer.function != NULL ) del_timer( &priv->timer ); + priv->timer.function = NULL; free_irq( dev->irq, dev ); TLan_FreeLists( dev ); TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name ); @@ -1132,8 +1143,8 @@ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); } -#if LINUX_KERNEL_VERSION > 0x20100 - priv->stats->tx_bytes += head_list->frameSize; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) + priv->stats.tx_bytes += head_list->frameSize; #endif head_list->cStat = TLAN_CSTAT_UNUSED; @@ -1252,8 +1263,8 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) skb_reserve( skb, 2 ); t = (void *) skb_put( skb, head_list->frameSize ); -#if LINUX_KERNEL_VERSION > 0x20100 - priv->stats->rx_bytes += head_list->frameSize; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) + priv->stats.rx_bytes += head_list->frameSize; #endif memcpy( t, head_buffer, head_list->frameSize ); @@ -1276,8 +1287,8 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) skb = (struct sk_buff *) head_list->buffer[9].address; head_list->buffer[9].address = 0; skb_trim( skb, head_list->frameSize ); -#if LINUX_KERNEL_VERSION > 0x20100 - priv->stats->rx_bytes += head_list->frameSize; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) + priv->stats.rx_bytes += head_list->frameSize; #endif skb->protocol = eth_type_trans( skb, dev ); @@ -1884,7 +1895,7 @@ TLan_ResetAdapter( struct device *dev ) u32 addr; u32 data; u8 data8; - + priv->tlanFullDuplex = FALSE; priv->phyOnline=0; /* 1. Assert reset bit. */ @@ -2015,7 +2026,7 @@ TLan_FinishReset( struct device *dev ) outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); } else { printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name ); - TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET ); + TLan_SetTimer( dev, (10*HZ), TLAN_TIMER_FINISH_RESET ); return; } @@ -2197,11 +2208,11 @@ void TLan_PhyPowerDown( struct device *dev ) TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value ); } - /* Wait for 5 jiffies (50 ms) and powerup + /* Wait for 50 ms and powerup * This is abitrary. It is intended to make sure the * tranceiver settles. */ - TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP ); + TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_PUP ); } /* TLan_PhyPowerDown */ @@ -2212,17 +2223,17 @@ void TLan_PhyPowerUp( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; u16 value; - + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering up PHY.\n", dev->name ); TLan_MiiSync( dev->base_addr ); value = MII_GC_LOOPBK; TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value ); - /* Wait for 50 jiffies (500 ms) and reset the + /* Wait for 500 ms and reset the * tranceiver. The TLAN docs say both 50 ms and * 500 ms, so do the longer, just in case */ - TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET ); + TLan_SetTimer( dev, (HZ/2), TLAN_TIMER_PHY_RESET ); } /* TLan_PhyPowerUp */ @@ -2247,10 +2258,10 @@ void TLan_PhyReset( struct device *dev ) } TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 ); - /* Wait for 50 jiffies (500 ms) and initialize. + /* Wait for 500 ms and initialize. * I don't remember why I wait this long. */ - TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK ); + TLan_SetTimer( dev, (HZ/2), TLAN_TIMER_PHY_START_LINK ); } /* TLan_PhyReset */ @@ -2293,13 +2304,13 @@ void TLan_PhyStartLink( struct device *dev ) TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 ); TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 ); - /* Wait for 400 jiffies (4 sec) for autonegotiation + /* Wait for 4 sec for autonegotiation * to complete. The max spec time is less than this * but the card need additional time to start AN. * .5 sec should be plenty extra. */ printk( "TLAN: %s: Starting autonegotiation.\n", dev->name ); - TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN ); + TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_PHY_FINISH_AN ); return; } @@ -2307,7 +2318,7 @@ void TLan_PhyStartLink( struct device *dev ) priv->phyNum = 0; data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data ); - TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN ); + TLan_SetTimer( dev, (4*(HZ/1000)), TLAN_TIMER_PHY_PDOWN ); return; } else if ( priv->phyNum == 0 ) { TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl ); @@ -2328,10 +2339,10 @@ void TLan_PhyStartLink( struct device *dev ) TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl ); } - /* Wait for 100 jiffies (1 sec) to give the tranceiver time + /* Wait for 1 sec to give the tranceiver time * to establish link. */ - TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET ); + TLan_SetTimer( dev, HZ, TLAN_TIMER_FINISH_RESET ); } /* TLan_PhyStartLink */ @@ -2352,11 +2363,11 @@ void TLan_PhyFinishAutoNeg( struct device *dev ) TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); if ( ! ( status & MII_GS_AUTOCMPLT ) ) { - /* Wait for 800 jiffies (8 sec) to give the process + /* Wait for 8 sec to give the process * more time. Perhaps we should fail after a while. */ printk( "TLAN: Giving autonegotiation more time.\n" ); - TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN ); + TLan_SetTimer( dev, (8*HZ), TLAN_TIMER_PHY_FINISH_AN ); return; } @@ -2374,7 +2385,7 @@ void TLan_PhyFinishAutoNeg( struct device *dev ) priv->phyNum = 0; data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data ); - TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN ); + TLan_SetTimer( dev, (400*(HZ/1000)), TLAN_TIMER_PHY_PDOWN ); return; } @@ -2388,9 +2399,9 @@ void TLan_PhyFinishAutoNeg( struct device *dev ) } } - /* Wait for 10 jiffies (100 ms). No reason in partiticular. + /* Wait for 100 ms. No reason in partiticular. */ - TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET ); + TLan_SetTimer( dev, (HZ/10), TLAN_TIMER_FINISH_RESET ); } /* TLan_PhyFinishAutoNeg */ diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h index 3c135e7471c2..6db99377e038 100644 --- a/drivers/net/tlan.h +++ b/drivers/net/tlan.h @@ -8,7 +8,8 @@ * by James Banks * * (C) 1997-1998 Caldera, Inc. - * + * (C) 1999-2000 Torben Mathiasen + * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * diff --git a/drivers/pci/oldproc.c b/drivers/pci/oldproc.c index 8531767f1aba..946427827e7c 100644 --- a/drivers/pci/oldproc.c +++ b/drivers/pci/oldproc.c @@ -36,6 +36,7 @@ struct pci_dev_info { */ struct pci_dev_info dev_info[] = { DEVICE( COMPAQ, COMPAQ_1280, "QVision 1280/p"), + DEVICE( COMPAQ, COMPAQ_6010, "Hot Plug PCI Bridge"), DEVICE( COMPAQ, COMPAQ_SMART2P, "Smart-2/P RAID Controller"), DEVICE( COMPAQ, COMPAQ_NETEL100,"Netelligent 10/100"), DEVICE( COMPAQ, COMPAQ_NETEL10, "Netelligent 10"), @@ -411,6 +412,8 @@ struct pci_dev_info dev_info[] = { DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"), DEVICE( RENDITION, RENDITION_VERITE,"Verite 1000"), DEVICE( RENDITION, RENDITION_VERITE2100,"Verite 2100"), + DEVICE( RCC, RCC_HE, "CNB20HE PCI Bridge"), + DEVICE( RCC, RCC_LE, "CNB30LE PCI Bridge"), DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"), DEVICE( TOSHIBA, TOSHIBA_TOPIC95,"ToPIC95"), DEVICE( TOSHIBA, TOSHIBA_TOPIC97,"ToPIC97"), diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1d77366d7c1b..b4fc7a5dddbd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -282,6 +282,14 @@ __initfunc(unsigned int pci_scan_bus(struct pci_bus *bus)) * all PCI-to-PCI bridges on this bus. */ pcibios_fixup_bus(bus); + /* + * The fixup code may have just found some peer pci bridges on this + * machine. Update the max variable if that happened so we don't + * get duplicate bus numbers. + */ + for(child=&pci_root; child; child=child->next) + max=((max > child->subordinate) ? max : child->subordinate); + for(dev=bus->devices; dev; dev=dev->sibling) /* * If it's a bridge, scan the bus behind it. @@ -291,6 +299,37 @@ __initfunc(unsigned int pci_scan_bus(struct pci_bus *bus)) unsigned int devfn = dev->devfn; unsigned short cr; + /* + * Check for a duplicate bus. If we already scanned + * this bus number as a peer bus, don't also scan it + * as a child bus + */ + if( + ((dev->vendor == PCI_VENDOR_ID_RCC) && + ((dev->device == PCI_DEVICE_ID_RCC_HE) || + (dev->device == PCI_DEVICE_ID_RCC_LE))) || + ((dev->vendor == PCI_VENDOR_ID_COMPAQ) && + (dev->device == PCI_DEVICE_ID_COMPAQ_6010)) || + ((dev->vendor == PCI_VENDOR_ID_INTEL) && + ((dev->device == PCI_DEVICE_ID_INTEL_82454NX) || + (dev->device == PCI_DEVICE_ID_INTEL_82451NX))) + ) + goto skip_it; + /* + * Read the existing primary/secondary/subordinate bus + * number configuration to determine if the PCI bridge + * has already been configured by the system. If so, + * check to see if we've already scanned this bus as + * a result of peer bus scanning, if so, skip this. + */ + pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses); + if ((buses & 0xFFFFFF) != 0) + { + for(child=pci_root.next;child;child=child->next) + if(child->number == ((buses >> 8) & 0xff)) + goto skip_it; + } + /* * Insert it into the tree of buses. */ @@ -363,6 +402,7 @@ __initfunc(unsigned int pci_scan_bus(struct pci_bus *bus)) pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses); } pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr); +skip_it: } /* @@ -380,6 +420,12 @@ struct pci_bus * __init pci_scan_peer_bridge(int bus) { struct pci_bus *b; + b = &pci_root; + while((b != NULL) && (bus != 0)) { + if(b->number == bus) + return(b); + b = b->next; + } b = kmalloc(sizeof(*b), GFP_KERNEL); memset(b, 0, sizeof(*b)); b->next = pci_root.next; diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 27dc05afd716..e3fd18778bd6 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3,7 +3,7 @@ * Author(s)......: Holger Smolinski * : Utz Bacher * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 */ #include @@ -26,7 +26,7 @@ #include #include -#include "../../../arch/s390/kernel/irq.h" +#include #include "dasd.h" #include @@ -38,12 +38,12 @@ #define CCW_READ_DEVICE_CHARACTERISTICS 0x64 -#define DASD_SSCH_RETRIES 5 +#define DASD_SSCH_RETRIES 2 /* This macro is a little tricky, but makes the code more easy to read... */ #define MATCH(info,ct,cm,dt,dm) ( \ -( info -> sid_data.cu_type ct ) && ( info -> sid_data.cu_model cm ) && \ -( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm ) ) +(( info -> sid_data.cu_type ct ) && ( info -> sid_data.cu_model cm )) && \ +(( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm )) ) /* Prototypes for the functions called from external */ static ssize_t dasd_read (struct file *, char *, size_t, loff_t *); @@ -344,14 +344,12 @@ fill_sizes (int di) dasd_blks[minor] = dasd_info[di]->sizes.kbytes; dasd_secsize[minor] = dasd_info[di]->sizes.bp_sector; dasd_blksize[minor] = dasd_info[di]->sizes.bp_block; - dasd_maxsecs[minor] = - ((PAGE_SIZE/sizeof(ccw1_t))-4)<sizes.s2b_shift; + dasd_maxsecs[minor] = 252<sizes.s2b_shift; dasd_blks[minor + 1] = dasd_info[di]->sizes.kbytes - (dasd_info[di]->sizes.first_sector >> 1); dasd_secsize[minor + 1] = dasd_info[di]->sizes.bp_sector; dasd_blksize[minor + 1] = dasd_info[di]->sizes.bp_block; - dasd_maxsecs[minor+1] = - ((PAGE_SIZE/sizeof(ccw1_t))-4)<sizes.s2b_shift; + dasd_maxsecs[minor+1] = 252<sizes.s2b_shift; } int @@ -554,6 +552,7 @@ check_type (dev_info_t * info) FUNCTION_ENTRY ("check_type"); #ifdef CONFIG_DASD_ECKD if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) || + MATCH (info, == 0x9343, ||1, == 0x9345, ||1) || MATCH (info, == 0x3990, ||1, == 0x3380, ||1)) { type = dasd_eckd; } else @@ -695,6 +694,8 @@ dasd_start_IO (cqr_t * cqr) int retries = DASD_SSCH_RETRIES; int di, irq; + dasd_debug (cqr); /* cqr */ + if (!cqr) { PRINT_WARN ("(start_IO) no cqr passed\n"); return -EINVAL; @@ -773,7 +774,7 @@ dasd_dump_sense (devstat_t * stat) { int sl, sct; printk (KERN_INFO PRINTK_HEADER - "-------------------I/O Error-----------------------------\n"); + "-------------------I/O resulted in unit check:-----------\n"); for (sl = 0; sl < 4; sl++) { printk (KERN_INFO PRINTK_HEADER "Sense:"); for (sct = 0; sct < 8; sct++) { @@ -790,17 +791,17 @@ dasd_do_chanq (void) dasd_chanq_t *qp = NULL; cqr_t *cqr; long flags; - int irq,di; + int irq; int tasks; atomic_set (&bh_scheduled, 0); - dasd_debug (0x44445f69); /* DD_i */ + dasd_debug (0xc4c40000); /* DD */ while ((tasks = atomic_read(&chanq_tasks)) != 0) { /* initialization and wraparound */ if (qp == NULL) { - dasd_debug (0x44445f68); /* DD_h */ + dasd_debug (0xc4c46df0); /* DD_0 */ qp = cq_head; if (!qp) { - dasd_debug (0x44445f45); /* DD_E */ + dasd_debug (0xc4c46ff1); /* DD?1 */ dasd_debug (tasks); PRINT_ERR("Mismatch of NULL queue pointer and " "still %d chanq_tasks to do!!\n" @@ -810,18 +811,9 @@ dasd_do_chanq (void) break; } } -/* Get the irq number: Ouch, FIXME!!!!! */ -#if 0 - di =(((unsigned long)qp)- - ((unsigned long)&(dasd_info[0]->queue))) - / sizeof(dasd_information_t); - dasd_debug(di); - irq = dasd_info[di]->info.irq; - dasd_debug(irq); -#endif /* Get first request */ + dasd_debug (qp); /* qp */ cqr = (cqr_t *) (qp->head); - dasd_debug ((unsigned long) cqr); /* cqr */ /* empty queue -> dequeue and proceed */ if (!cqr) { dasd_chanq_t *nqp = qp->next_q; @@ -832,33 +824,35 @@ dasd_do_chanq (void) /* process all requests on that queue */ do { cqr_t *next; - dasd_debug (0x44445f64); /* DD_d */ dasd_debug ((unsigned long) cqr); /* cqr */ if (cqr->magic != DASD_MAGIC) { - dasd_debug (0x44445f65); /* DD_e */ - PRINT_WARN ("do_cq:magic mismatch %p\n", cqr); + dasd_debug (0xc4c46ff2); /* DD?2 */ + panic ( PRINTK_HEADER "do_cq:" + "magic mismatch %p -> %x\n", + cqr, cqr -> magic); break; } irq = dasd_info[cqr->devindex]->info.irq; s390irq_spin_lock_irqsave (irq, flags); switch (atomic_read (&cqr->status)) { case CQR_STATUS_IN_IO: - dasd_debug (0x44445f48); /* DD_I */ + dasd_debug (0xc4c4c9d6); /* DDIO */ cqr = NULL; break; case CQR_STATUS_QUEUED: - dasd_debug (0x44445f53); /* DD_S */ + dasd_debug (0xc4c4e2e3); /* DDST */ if (dasd_start_IO (cqr) == 0) { atomic_dec (&chanq_tasks); cqr = NULL; } break; case CQR_STATUS_ERROR: - dasd_debug (0x44445f54); /* DD_E */ + dasd_debug (0xc4c4c5d9); /* DDER */ dasd_dump_sense (cqr->dstat); - if (cqr->retries++ < 5) { + if ( ++ cqr->retries < 2 ) { atomic_set (&cqr->status, CQR_STATUS_QUEUED); + dasd_debug (0xc4c4e2e3); /* DDST */ if (dasd_start_IO (cqr) == 0) { atomic_dec (&chanq_tasks); cqr = NULL; @@ -870,14 +864,14 @@ dasd_do_chanq (void) break; case CQR_STATUS_DONE: next = cqr->next; - dasd_debug (0x44445f44); /* DD_D */ + dasd_debug (0xc4c49692); /* DDok */ dasd_end_cqr (cqr, 1); atomic_dec (&chanq_tasks); cqr = next; break; case CQR_STATUS_FAILED: next = cqr->next; - dasd_debug (0x44445f45); /* DD_F */ + dasd_debug (0xc4c47a7a); /* DD:: */ dasd_end_cqr (cqr, 0); atomic_dec (&chanq_tasks); cqr = next; @@ -893,7 +887,7 @@ dasd_do_chanq (void) spin_lock (&io_request_lock); do_dasd_request (); spin_unlock (&io_request_lock); - dasd_debug (0x44445f6f); /* DD_o */ + dasd_debug (0xc4c46d6d); /* DD__ */ } /* @@ -901,6 +895,9 @@ dasd_do_chanq (void) We use it to feed the chanqs. This implementation assumes we are serialized by the io_request_lock. */ + +#define QUEUE_THRESHOLD 5 + void do_dasd_request (void) { @@ -916,23 +913,25 @@ do_dasd_request (void) long flags; int irq; - __asm__ ("lr %0,14":"=d" (caller)); - dasd_debug (0x44525f69); /* DR_i */ - dasd_debug ((unsigned long) caller); /* calleraddres */ + dasd_debug (0xc4d90000); /* DR */ + dasd_debug (__builtin_return_address(0)); /* calleraddres */ prev = NULL; for (req = CURRENT; req != NULL; req = next) { next = req->next; di = DEVICE_NR (req->rq_dev); dasd_debug ((unsigned long) req); /* req */ - dasd_debug (0x44520000 + /* DR## */ - ((((di/16)<9?(di/16)+'0':(di/16)+'a'))<<8) + - (((di%16)<9?(di%16)+'0':(di%16)+'a'))); + dasd_debug (0xc4d90000 + /* DR## */ + ((((di/16)<9?(di/16)+0xf0:(di/16)+0xc1))<<8) + + (((di%16)<9?(di%16)+0xf0:(di%16)+0xc1))); irq = dasd_info[di]->info.irq; s390irq_spin_lock_irqsave (irq, flags); q = &dasd_info[di]->queue; busy[di] = busy[di]||(atomic_read(&q->flags)&DASD_CHANQ_BUSY); if ( !busy[di] || ((!broken[di]) && (req->nr_sectors >= QUEUE_SECTORS))) { +#if 0 + if ( q -> queued_requests < QUEUE_THRESHOLD ) { +#endif if (prev) { prev->next = next; } else { @@ -940,39 +939,41 @@ do_dasd_request (void) } req->next = NULL; if (req == &blk_dev[MAJOR_NR].plug) { - dasd_debug (0x44525f75); /* DR_u */ + dasd_debug (0xc4d99787); /* DRpg */ goto cont; } cqr = dasd_cqr_from_req (req); if (!cqr) { - dasd_debug (0x44525f65); /* DR_e */ + dasd_debug (0xc4d96ff1); /* DR?1 */ dasd_end_request (req, 0); goto cont; } - dasd_debug (0x44525f71); /* DR_q */ dasd_debug ((unsigned long) cqr); /* cqr */ dasd_chanq_enq (q, cqr); - if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) { + if (!(atomic_read (&q->flags) & + DASD_CHANQ_ACTIVE)) { cql_enq_head (q); } if (!busy[di]) { - if (dasd_start_IO (cqr) == 0) - dasd_debug (0x44525f73); /* DR_s */ - else { + dasd_debug (0xc4d9e2e3); /* DRST */ + if (dasd_start_IO (cqr) != 0) { atomic_inc (&chanq_tasks); schedule_bh (dasd_do_chanq); busy[di] = 1; } } +#if 0 + } +#endif } else { - dasd_debug (0x44525f62); /* DR_b */ + dasd_debug (0xc4d9c2d9); /* DRBR */ broken[di] = 1; prev = req; } cont: s390irq_spin_unlock_irqrestore (irq, flags); } - dasd_debug (0x44525f6f); /* DR_o */ + dasd_debug (0xc4d96d6d); /* DR__ */ } void @@ -983,34 +984,35 @@ dasd_handler (int irq, void *ds, struct pt_regs *regs) cqr_t *cqr; int done_fast_io = 0; - dasd_debug (0x44485f69); /* DH_i */ + dasd_debug (0xc4c80000); /* DH */ if (!stat) PRINT_ERR ("handler called without devstat"); ip = stat->intparm; dasd_debug (ip); /* intparm */ switch (ip) { /* filter special intparms... */ case 0x00000000: /* no intparm: unsolicited interrupt */ - dasd_debug (0x44485f30); /* DH_0 */ + dasd_debug (0xc4c8a489); /* DHui */ PRINT_INFO ("Unsolicited interrupt on device %04X\n", stat->devno); dasd_dump_sense (stat); return; default: if (ip & 0x80000001) { + dasd_debug (0xc4c8a489); /* DHui */ PRINT_INFO ("Spurious interrupt %08x on device %04X\n", ip, stat->devno); return; } cqr = (cqr_t *) ip; if (cqr->magic != DASD_MAGIC) { - dasd_debug (0x44485f65); /* DH_e */ - PRINT_WARN ("handler:magic mismatch on %p %08x\n", + dasd_debug (0xc4c86ff1); /* DH?1 */ + PRINT_ERR ("handler:magic mismatch on %p %08x\n", cqr, cqr->magic); return; } asm volatile ("STCK %0":"=m" (cqr->stopclk)); if (stat->cstat == 0x00 && stat->dstat == 0x0c) { - dasd_debug (0x44486f6b); /* DHok */ + dasd_debug (0xc4c89692); /* DHok */ if (atomic_compare_and_swap (CQR_STATUS_IN_IO, CQR_STATUS_DONE, &cqr->status)) { @@ -1018,31 +1020,23 @@ dasd_handler (int irq, void *ds, struct pt_regs *regs) atomic_read (&cqr->status)); } if (cqr->next) { - dasd_debug (0x44485f6e); /* DH_n */ + dasd_debug (0xc4c8e2e3); /* DHST */ if (dasd_start_IO (cqr->next) == 0) done_fast_io = 1; } break; } - dasd_debug (0x44482121); /* DH!! */ + dasd_debug (0xc4c8c5d9); /* DHER */ if (!cqr->dstat) cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC); if (cqr->dstat) { memcpy (cqr->dstat, stat, sizeof (devstat_t)); - dasd_dump_sense (cqr->dstat); } else { PRINT_ERR ("no memory for dtstat\n"); } /* errorprocessing */ - if (cqr->retries < DASD_MAX_RETRIES) { - dasd_debug (0x44485f72); /* DH_r */ atomic_set (&cqr->status, CQR_STATUS_ERROR); - } else { - dasd_debug (0x44485f6e); /* DH_f */ - atomic_set (&cqr->status, CQR_STATUS_FAILED); - } - } if (done_fast_io == 0) atomic_clear_mask (DASD_CHANQ_BUSY, @@ -1050,16 +1044,17 @@ dasd_handler (int irq, void *ds, struct pt_regs *regs) queue.flags); if (cqr->flags & DASD_DO_IO_SLEEP) { - dasd_debug (0x44485f77); /* DH_w */ + dasd_debug (0xc4c8a6a4); /* DHwu */ dasd_wakeup (); } else if (! (cqr->options & DOIO_WAIT_FOR_INTERRUPT) ){ - dasd_debug (0x44485f73); /* DH_s */ + dasd_debug (0xc4c8a293); /* DHsl */ atomic_inc (&chanq_tasks); schedule_bh (dasd_do_chanq); } else { + dasd_debug (0x64686f6f); /* DH_g */ dasd_debug (cqr->flags); /* DH_g */ - dasd_debug (0x44485f6f); /* DH_g */ } + dasd_debug (0xc4c86d6d); /* DHwu */ } static int diff --git a/drivers/s390/block/dasd_ccwstuff.c b/drivers/s390/block/dasd_ccwstuff.c index a0d2893ed72b..280ecd72e96f 100644 --- a/drivers/s390/block/dasd_ccwstuff.c +++ b/drivers/s390/block/dasd_ccwstuff.c @@ -2,7 +2,7 @@ * File...........: linux/drivers/s390/block/dasd_ccwstuff.c * Author(s)......: Holger Smolinski * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 */ #include @@ -337,6 +337,7 @@ dasd_chanq_enq (dasd_chanq_t * q, cqr_t * cqr) q->head = cqr; cqr->next = NULL; q->tail = cqr; + q->queued_requests ++; if (atomic_compare_and_swap(CQR_STATUS_FILLED, CQR_STATUS_QUEUED, &cqr->status)) { @@ -368,6 +369,7 @@ dasd_chanq_deq (dasd_chanq_t * q, cqr_t * cqr) q->tail = prev; } cqr->next = NULL; + q->queued_requests --; return release_cqr(cqr); } diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 283b81fc99b9..702a06295d54 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2,7 +2,7 @@ * File...........: linux/drivers/s390/block/dasd_eckd.c * Author(s)......: Holger Smolinski * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 */ #include @@ -15,7 +15,7 @@ #include #include -#include "../../../arch/s390/kernel/irq.h" +#include #include "dasd_types.h" #include "dasd_ccwstuff.h" @@ -347,17 +347,17 @@ recs_per_track (dasd_eckd_characteristics_t * rdc, unsigned int kl, unsigned int dl) { int rpt = 0; - if (rdc->formula == 0x01) { + int dn; + switch ( rdc -> dev_type ) { + case 0x3380: if (kl) return 1499 / (15 + 7 + ceil_quot (kl + 12, 32) + ceil_quot (dl + 12, 32)); else return 1499 / (15 + ceil_quot (dl + 12, 32)); - - } - if (rdc->formula == 0x02) { - int dn = ceil_quot (dl + 6, 232) + 1; + case 0x3390: + dn = ceil_quot (dl + 6, 232) + 1; if (kl) { int kn = ceil_quot (kl + 6, 232) + 1; return 1729 / (10 + @@ -366,6 +366,16 @@ recs_per_track (dasd_eckd_characteristics_t * rdc, } else return 1729 / (10 + 9 + ceil_quot (dl + 6 * dn, 34)); + case 0x9345: + dn = ceil_quot (dl + 6, 232) + 1; + if (kl) { + int kn = ceil_quot (kl + 6, 232) + 1; + return 1420 / (18 + + 7 + ceil_quot (kl + 6 * kn, 34) + + ceil_quot (dl + 6 * dn, 34)); + } else + return 1420 / (18 + + 7 + ceil_quot (dl + 6 * dn, 34)); } return rpt; } @@ -403,9 +413,11 @@ define_extent (ccw1_t * de_ccw, case DASD_ECKD_CCW_READ_CKD_MT: case DASD_ECKD_CCW_READ_COUNT: data->mask.perm = 0x1; + data->attributes.operation = 0x3; /* enable seq. caching */ break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: + data->attributes.operation = 0x3; /* enable seq. caching */ break; case DASD_ECKD_CCW_WRITE_CKD: case DASD_ECKD_CCW_WRITE_CKD_MT: @@ -914,8 +926,11 @@ dasd_eckd_read_count (int di) rc = dasd_start_IO ( rw_cp ); s390irq_spin_unlock_irqrestore (irq, flags); retries --; - } while ( ( ( (cs=atomic_read(&rw_cp->status)) != CQR_STATUS_DONE) || - rc ) && retries ); + cs = atomic_read(&rw_cp->status); + if ( cs != CQR_STATUS_DONE && retries == 5 ) { + dasd_eckd_print_error(rw_cp->dstat); + } + } while ( ( ( cs != CQR_STATUS_DONE) || rc ) && retries ); if ( ( rc || cs != CQR_STATUS_DONE) ) { if ( ( cs == CQR_STATUS_ERROR ) && ( rw_cp -> dstat -> ii.sense.data[1] == 0x08 ) ) { diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 786cf40724de..ae6ff68c13bf 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -92,11 +92,11 @@ dasd_proc_read_devices ( char * buf, char **start, off_t off, int len, int d) if ( len >= PAGE_SIZE - 80 ) len += sprintf ( buf + len, "terminated...\n"); len += sprintf ( buf + len, - "%04X %3d %5d /dev/dd%04X", + "%04X %3d %5d /dev/dasd%c", dasd_info[i]->info.devno, DASD_MAJOR, i << PARTN_BITS, - i ); + 'a' + i ); if (info->flags == DASD_NOT_FORMATTED) { len += sprintf ( buf + len, " n/a"); } else { diff --git a/drivers/s390/block/dasd_profile.c b/drivers/s390/block/dasd_profile.c index 2d613c6a2fb6..9bd52c9bfb6f 100644 --- a/drivers/s390/block/dasd_profile.c +++ b/drivers/s390/block/dasd_profile.c @@ -167,15 +167,14 @@ dasd_debug ( unsigned long tag ) dasd_debug_actual = (dasd_debug_entry *) ( (unsigned long) dasd_debug_area + ( ( (unsigned long)dasd_debug_actual - - (unsigned long)dasd_debug_area ) % 1808 ) ); + (unsigned long)dasd_debug_area ) % PAGE_SIZE ) ); d = dasd_debug_actual ++; spin_unlock_irqrestore(&debug_lock,flags); /* write CPUID to lowest 12 bits of clock... */ - __asm__ __volatile__ ( "STCK %0\n" - "ST 14,%1\n" - :"=m" (d->u.clock), - "=m" (d->caller_address)); + __asm__ __volatile__ ( "STCK %0" + :"=m" (d->u.clock)); d->tag = tag; + d -> caller_address = __builtin_return_address(0); d->u.s.cpu = smp_processor_id(); } diff --git a/drivers/s390/block/dasd_types.h b/drivers/s390/block/dasd_types.h index 46842f2d316e..e75d6f30953c 100644 --- a/drivers/s390/block/dasd_types.h +++ b/drivers/s390/block/dasd_types.h @@ -1,10 +1,9 @@ -/* 456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789. - * IBM CONFIDENTIAL +/* * File...........: linux/drivers/s390/block/dasd_types.h * Author.........: Holger Smolinski * Created........: 08/31/1999 * Last Modified..: 09/29/1999 - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * List of Changes: - Initial Release as of 09/29/1999 @@ -26,7 +25,7 @@ #include -#include "../../../arch/s390/kernel/irq.h" +#include #define CCW_DEFINE_EXTENT 0x63 #define CCW_LOCATE_RECORD 0x43 @@ -65,9 +64,8 @@ struct { unsigned char removable:1; unsigned char shared:1; unsigned char zero1:1; - unsigned char bam:1; - unsigned char hpsa:1; - unsigned char zeros:2; + unsigned char mam:1; + unsigned char zeros:3; } __attribute__ ((packed)) bits; } __attribute__ ((packed)) features; __u8 dev_class; @@ -76,7 +74,7 @@ struct { __u32 blk_per_cycl; __u32 blk_per_bound; __u32 blk_bdsa; - __u32 blk_hpsa; + __u32 reserved0; __u16 reserved1; __u16 blk_ce; __u32 reserved2; @@ -248,7 +246,7 @@ struct dasd_chanq_t { volatile cqr_t *tail; spinlock_t q_lock; /* lock for queue operations */ spinlock_t f_lock; /* lock for flag operations */ - long lockflags; + int queued_requests; atomic_t flags; struct dasd_chanq_t *next_q; /* pointer to next queue */ } __attribute__ ((packed, aligned (16))) diff --git a/drivers/s390/block/mdisk.c b/drivers/s390/block/mdisk.c index 6b68fe70a538..f710cee85084 100644 --- a/drivers/s390/block/mdisk.c +++ b/drivers/s390/block/mdisk.c @@ -3,7 +3,7 @@ * VM minidisk device driver. * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com) */ @@ -37,7 +37,7 @@ char kernel_version [] = UTS_RELEASE; #include /* virt_to_phys */ /* Added statement HSM 12/03/99 */ -#include "../../../arch/s390/kernel/irq.h" +#include #define MAJOR_NR MDISK_MAJOR /* force definitions on in blk.h */ @@ -125,6 +125,7 @@ __initfunc(void mdisk_setup(char *str,int *ints)) vdev = size = offset = 0; if (!isxdigit(*cur)) goto syntax_error; vdev = simple_strtoul(cur,&cur,16); + if (*cur != 0 && *cur != ',') { if (*cur++ != ':') goto syntax_error; if (!isxdigit(*cur)) goto syntax_error; size = simple_strtoul(cur,&cur,16); @@ -139,6 +140,7 @@ __initfunc(void mdisk_setup(char *str,int *ints)) } } if (*cur != ',' && *cur != 0) goto syntax_error; + } if (*cur == ',') cur++; if (i >= MDISK_DEVS) { printk(KERN_WARNING "mnd: too many devices\n"); @@ -159,6 +161,8 @@ syntax_error: return; } + + /* * Open and close */ @@ -297,6 +301,101 @@ dia250(void* iob,int cmd) return rc; } +/* + * The device characteristics function + */ + +static __inline__ int +dia210(void* devchar) +{ + int rc; + + devchar = (void*) virt_to_phys(devchar); + + asm volatile (" lr 2,%1\n" + " .long 0x83200210\n" + " ipm %0\n" + " srl %0,28" + : "=d" (rc) + : "d" (devchar) + : "2" ); + return rc; +} +/* + * read the label of a minidisk and extract its characteristics + */ + +static __inline__ int +mdisk_read_label (mdisk_Dev *dev, int i) +{ + static mdisk_dev_char_t devchar; + static long label[1024]; + int block, b; + int rc; + mdisk_bio_t *bio; + + devchar.dev_nr = dev -> vdev; + devchar.rdc_len = sizeof(mdisk_dev_char_t); + + if (dia210(&devchar) == 0) { + if (devchar.vdev_class == DEV_CLASS_FBA) { + block = 2; + } + else { + block = 3; + } + bio = dev->bio; + rc = mdisk_term_io(dev); + for (b=512;b<4097;b=b*2) { + rc = mdisk_init_io(dev, b, 0, 64); + if (rc > 4) { + continue; + } + memset(&bio[0], 0, sizeof(mdisk_bio_t)); + bio[0].type = MDISK_READ_REQ; + bio[0].block_number = block; + bio[0].buffer = virt_to_phys(&label); + dev->nr_bhs = 1; + if (mdisk_rw_io_clustered(dev, + &bio[0], + 1, + (unsigned long) dev, + MDISK_SYNC) + == 0 ) { + if (label[0] != 0xc3d4e2f1) { /* CMS1 */ + printk ( KERN_WARNING "mnd: %4lX " + "is not CMS format\n", + mdisk_setup_data.vdev[i]); + rc = mdisk_term_io(dev); + return 1; + } + if (label[13] == 0) { + printk ( KERN_WARNING "mnd: %4lX " + "is not reserved\n", + mdisk_setup_data.vdev[i]); + rc = mdisk_term_io(dev); + return 2; + } + mdisk_setup_data.size[i] = + (label[7] - 1 - label[13]) * + (label[3] >> 9) >> 1; + mdisk_setup_data.blksize[i] = label[3]; + mdisk_setup_data.offset[i] = label[13] + 1; + rc = mdisk_term_io(dev); + return rc; + } + rc = mdisk_term_io(dev); + } + printk ( KERN_WARNING "mnd: Cannot read label of %4lX " + "- is it formatted?\n", + mdisk_setup_data.vdev[i]); + return 3; + } + return 4; +} + + + /* * Init of minidisk device */ @@ -365,11 +464,11 @@ mdisk_rw_io_clustered (mdisk_Dev *dev, iob->bio_list = virt_to_phys(bio_array); rc = dia250(iob,RW_BIO); - rc = (rc == 8) ? 0 : rc; return rc; } + /* * this handles a clustered request in success case * all buffers are detach and marked uptodate to the kernel @@ -507,7 +606,7 @@ void mdisk_request(void) #else MDISK_ASYNC #endif - )) != 0 ) { + )) > 8 ) { printk(KERN_WARNING "mnd%c: %s request failed rc %d" " sector %ld nr_sectors %ld \n", DEVICE_NR(CURRENT_DEV), @@ -524,6 +623,9 @@ void mdisk_request(void) #ifdef CONFIG_MDISK_SYNC mdisk_end_request(dev->nr_bhs); #else + if (rc == 0) + mdisk_end_request(dev->nr_bhs); + else return; #endif } @@ -627,8 +729,7 @@ __initfunc(int mdisk_init(void)) max_sectors[MAJOR_NR] = mdisk_maxsectors; for (i=0;ibio=mdisk_bio[i]; dev->iob=&mdisk_iob[i]; dev->vdev = mdisk_setup_data.vdev[i]; + + if ( mdisk_setup_data.size[i] == 0 ) + rc = mdisk_read_label(dev, i); dev->size = mdisk_setup_data.size[i] * 2; /* buffer 512 b */ dev->blksize = mdisk_setup_data.blksize[i]; dev->tqueue.routine = do_mdisk_bh; @@ -658,6 +762,7 @@ __initfunc(int mdisk_init(void)) dev->blkmult==4?2: dev->blkmult==8?3:-1; + mdisk_sizes[i] = mdisk_setup_data.size[i]; mdisk_blksizes[i] = mdisk_setup_data.blksize[i]; mdisk_hardsects[i] = mdisk_setup_data.blksize[i]; diff --git a/drivers/s390/block/mdisk.h b/drivers/s390/block/mdisk.h index 64b558eddf8e..864c4b9cd379 100644 --- a/drivers/s390/block/mdisk.h +++ b/drivers/s390/block/mdisk.h @@ -3,14 +3,14 @@ * VM minidisk device driver. * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com) */ #include #include -#define MDISK_DEVS 4 /* for disks */ +#define MDISK_DEVS 8 /* for disks */ #define MDISK_RAHEAD 8 /* read ahead */ #define MDISK_BLKSIZE 1024 /* 1k blocks */ #define MDISK_HARDSECT 512 /* FIXME -- 512 byte blocks */ @@ -25,8 +25,8 @@ #define MDISK_WRITE_REQ 0x01 #define MDISK_READ_REQ 0x02 -#define MDISK_SYNC 0x01 -#define MDISK_ASYNC 0x03 +#define MDISK_SYNC 0x00 +#define MDISK_ASYNC 0x02 #define INIT_BIO 0x00 #define RW_BIO 0x01 #define TERM_BIO 0x02 @@ -68,4 +68,27 @@ typedef struct { u32 spare3[5]; } mdisk_rw_io_t; +/* + * low level definitions for Diagnose 210 + */ + +#define DEV_CLASS_FBA 0x01 + +/* + * Data structures for Diagnose 210 + */ + +typedef struct { + u16 dev_nr; + u16 rdc_len; + u8 vdev_class; + u8 vdev_type; + u8 vdev_status; + u8 vdev_flags; + u8 rdev_class; + u8 rdev_type; + u8 rdev_model; + u8 rdev_features; +} mdisk_dev_char_t; + diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 0652a3c726c3..89c97759db6e 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -3,7 +3,7 @@ * 3215 line mode terminal driver. * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), */ @@ -22,7 +22,7 @@ #include #include "../../../arch/s390/kernel/cpcmd.h" -#include "../../../arch/s390/kernel/irq.h" +#include #define NR_3215 1 #define NR_3215_REQ (4*NR_3215) @@ -42,6 +42,7 @@ #define RAW3215_CLOSING 32 /* set while in close process */ #define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */ #define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */ +#define RAW3215_BH_PENDING 256 /* indication for bh scheduling */ struct _raw3215_info; /* forward declaration ... */ @@ -51,7 +52,7 @@ int raw3215_condevice = -1; /* preset console device */ * Request types for a 3215 device */ typedef enum { - RAW3215_READ, RAW3215_WRITE + RAW3215_FREE, RAW3215_READ, RAW3215_WRITE } raw3215_type; /* @@ -129,6 +130,9 @@ extern inline raw3215_req *raw3215_alloc_req(void) { extern inline void raw3215_free_req(raw3215_req *req) { unsigned long flags; + if (req->type == RAW3215_FREE) + return; /* don't free a free request */ + req->type = RAW3215_FREE; spin_lock_irqsave(&raw3215_freelist_lock, flags); req->next = raw3215_freelist; raw3215_freelist = req; @@ -208,12 +212,6 @@ static int raw3215_mk_write_ccw(raw3215_info *raw, raw3215_req *req) ix = (ix + count) & (RAW3215_BUFFER_SIZE - 1); ccw++; } - if (ccw > req->ccws) - ccw[-1].flags |= 0x40; /* use command chaining */ - ccw->cmd_code = 0x03; /* nop at the end */ - ccw->flags = 0x20; /* ignore incorrect length */ - ccw->cda = NULL; /* no data address for nop */ - ccw->count = 0; /* no count for nop */ return len; } @@ -226,14 +224,9 @@ static void raw3215_mk_read_ccw(raw3215_info *raw, raw3215_req *req) ccw = req->ccws; ccw->cmd_code = 0x0A; /* read inquiry */ - ccw->flags = 0x60; /* ignore incorrect length & command chaining */ + ccw->flags = 0x20; /* ignore incorrect length */ ccw->count = 160; ccw->cda = (void *) virt_to_phys(raw->inbuf); - ccw++; - ccw->cmd_code = 0x03; /* nop at the end */ - ccw->flags = 0x20; /* ignore incorrect length ind. */ - ccw->cda = NULL; /* no data address for nop */ - ccw->count = 0; /* no count for nop */ } /* @@ -335,6 +328,7 @@ static void raw3215_softint(void *data) raw = (raw3215_info *) data; s390irq_spin_lock_irqsave(raw->irq, flags); raw3215_try_io((raw3215_info *) data); + raw->flags &= ~RAW3215_BH_PENDING; s390irq_spin_unlock_irqrestore(raw->irq, flags); /* Check for pending message from raw3215_irq */ if (raw->message != NULL) { @@ -351,6 +345,21 @@ static void raw3215_softint(void *data) } } +/* + * Function to safely add raw3215_softint to tq_immediate. + * The s390irq spinlock must be held. + */ +static inline void raw3215_sched_bh(raw3215_info *raw) +{ + if (raw->flags & RAW3215_BH_PENDING) + return; /* already pending */ + raw->flags |= RAW3215_BH_PENDING; + raw->tqueue.routine = raw3215_softint; + raw->tqueue.data = raw; + queue_task(&raw->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + /* * Find the raw3215_info structure associated with irq */ @@ -391,23 +400,10 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) "(dev %i, dev sts 0x%2x, sch sts 0x%2x)"; raw->msg_dstat = dstat; raw->msg_cstat = cstat; - queue_task(&raw->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); + raw3215_sched_bh(raw); } } if (dstat & 0x01) { /* we got a unit exception */ -#if 0 - raw = raw3215_find_info(irq); - if (raw != NULL) { - raw->message = KERN_WARNING - "Got a unit exception in raw3215_irq " - "(dev %i, dev sts 0x%2x, sch sts 0x%2x)"; - raw->msg_dstat = dstat; - raw->msg_cstat = cstat; - queue_task(&raw->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); - } -#endif dstat &= ~0x01; /* we can ignore it */ } switch (dstat) { @@ -423,8 +419,7 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) raw->queued_read = req; if (MACHINE_IS_P390) memset(raw->inbuf, 0, RAW3215_INBUF_SIZE); - queue_task(&raw->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); + raw3215_sched_bh(raw); break; case 0x08: case 0x0C: @@ -501,28 +496,26 @@ static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) wake_up_interruptible(&raw->empty_wait); raw->empty_wait = NULL; } - queue_task(&raw->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); + raw3215_sched_bh(raw); break; default: - if (dstat & 0x04) { /* Strange interrupt, I'll do my best to clean up */ - if (req != NULL && req->info != NULL && - req->type == RAW3215_WRITE) { - req->info->count -= ((req->end - req->start) & + if ((raw = raw3215_find_info(irq)) == NULL) + return; /* That shouldn't happen ... */ + if (raw == NULL) break; + if (req != NULL && req->type != RAW3215_FREE) { + if (req->type == RAW3215_WRITE) + raw->count -= ((req->end - req->start) & (RAW3215_BUFFER_SIZE-1))+1; + raw->flags &= ~RAW3215_WORKING; + raw3215_free_req(req); } - } - raw = raw3215_find_info(irq); - if (raw != NULL) { raw->message = KERN_WARNING "Spurious interrupt in in raw3215_irq " "(dev %i, dev sts 0x%2x, sch sts 0x%2x)"; raw->msg_dstat = dstat; raw->msg_cstat = cstat; - queue_task(&raw->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); - } + raw3215_sched_bh(raw); } return; } @@ -708,20 +701,21 @@ raw3215_find_dev(int number) int irq; int count; - irq = 0; + irq = get_irq_first(); count = 0; - while (count <= number && get_dev_info(irq, &dinfo) != -ENODEV) { + while (count <= number && irq != -ENODEV) { + if (get_dev_info(irq, &dinfo) == -ENODEV) + break; if (dinfo.devno == raw3215_condevice || - dinfo.sid_data.cu_type == 0x3215) + dinfo.sid_data.cu_type == 0x3215) { count++; - irq++; - } - if (count <= number) - irq = -1; /* console not found */ - else - irq--; + if (count > number) return irq; } + irq = get_irq_next(irq); + } + return -1; /* console not found */ +} #ifdef CONFIG_3215_CONSOLE @@ -770,7 +764,7 @@ void con3215_unblank(void) raw = raw3215[0]; /* console 3215 is the first one */ s390irq_spin_lock_irqsave(raw->irq, flags); - while (RAW3215_BUFFER_SIZE > raw->count) { + while (raw->count > 0) { /* there might be a request pending */ raw->flags |= RAW3215_FLUSHING; raw3215_try_io(raw); diff --git a/drivers/s390/char/hwc.h b/drivers/s390/char/hwc.h index 86eb03a4a7c5..2ed34ce04288 100644 --- a/drivers/s390/char/hwc.h +++ b/drivers/s390/char/hwc.h @@ -13,54 +13,53 @@ #ifndef __HWC_H__ #define __HWC_H__ - #define ET_OpCmd 0x01 #define ET_Msg 0x02 #define ET_StateChange 0x08 #define ET_PMsgCmd 0x09 #define ET_CntlProgOpCmd 0x20 - #define ET_OpCmd_Mask 0x80000000 #define ET_Msg_Mask 0x40000000 #define ET_StateChange_Mask 0x01000000 #define ET_PMsgCmd_Mask 0x00800000 #define ET_CtlProgOpCmd_Mask 0x00000001 - #define GMF_DOM 0x8000 #define GMF_SndAlrm 0x4000 #define GMF_HoldMsg 0x2000 - #define LTF_CntlText 0x8000 #define LTF_LabelText 0x4000 #define LTF_DataText 0x2000 #define LTF_EndText 0x1000 #define LTF_PromptText 0x0800 - #define HWC_COMMAND_INITIATED 0 #define HWC_BUSY 2 #define HWC_NOT_OPERATIONAL 3 - #define HWC_CMDW_READDATA 0x00770005 #define HWC_CMDW_WRITEDATA 0x00760005 #define HWC_CMDW_WRITEMASK 0x00780005 - #define GDS_ID_MDSMU 0x1310 + #define GDS_ID_MDSRouteInfo 0x1311 + #define GDS_ID_AgUnWrkCorr 0x1549 + #define GDS_ID_SNACondReport 0x1532 + #define GDS_ID_CPMSU 0x1212 + #define GDS_ID_RoutTargInstr 0x154D + #define GDS_ID_OpReq 0x8070 -#define GDS_ID_TextCmd 0x1320 +#define GDS_ID_TextCmd 0x1320 #define GDS_KEY_SelfDefTextMsg 0x31 @@ -76,9 +75,9 @@ typedef struct { _EBUF_HEADER -} __attribute__((packed)) evbuf_t; - +} __attribute__ ((packed)) +evbuf_t; #define _MDB_HEADER u16 length; \ u16 type; \ @@ -106,32 +105,45 @@ typedef struct { typedef struct { _GO_HEADER -} __attribute__((packed)) go_t; +} __attribute__ ((packed)) + +go_t; typedef struct { go_t go; -} __attribute__((packed)) mdb_body_t; +} __attribute__ ((packed)) + +mdb_body_t; typedef struct { _MDB_HEADER mdb_body_t mdb_body; -} __attribute__((packed)) mdb_t; +} __attribute__ ((packed)) + +mdb_t; typedef struct { _EBUF_HEADER mdb_t mdb; -} __attribute__((packed)) msgbuf_t; +} __attribute__ ((packed)) + +msgbuf_t; typedef struct { _HWCB_HEADER msgbuf_t msgbuf; -} __attribute__((packed)) write_hwcb_t; +} __attribute__ ((packed)) + +write_hwcb_t; typedef struct { _MTO_HEADER -} __attribute__((packed)) mto_t; +} __attribute__ ((packed)) + +mto_t; -static write_hwcb_t write_hwcb_template = { +static write_hwcb_t write_hwcb_template = +{ sizeof(write_hwcb_t), 0x00, { @@ -154,20 +166,21 @@ static write_hwcb_t write_hwcb_template = { { sizeof(go_t), 0x0001 + } } } } }; -static mto_t mto_template = { +static mto_t mto_template = +{ sizeof(mto_t), 0x0004, LTF_EndText, 0x00 }; - typedef u32 _hwcb_mask_t; typedef struct { @@ -178,9 +191,12 @@ typedef struct { _hwcb_mask_t cp_send_mask; _hwcb_mask_t hwc_receive_mask; _hwcb_mask_t hwc_send_mask; -} __attribute__((packed)) init_hwcb_t; +} __attribute__ ((packed)) + +init_hwcb_t; -static init_hwcb_t init_hwcb_template = { +static init_hwcb_t init_hwcb_template = +{ sizeof(init_hwcb_t), 0x00, { @@ -195,7 +211,6 @@ static init_hwcb_t init_hwcb_template = { ET_Msg_Mask }; - #define _GDS_VECTOR_HEADER u16 length; \ u16 gds_id; @@ -204,17 +219,24 @@ static init_hwcb_t init_hwcb_template = { typedef struct { _GDS_VECTOR_HEADER -} __attribute__((packed)) gds_vector_t; +} __attribute__ ((packed)) + +gds_vector_t; typedef struct { _GDS_SUBVECTOR_HEADER -} __attribute__((packed)) gds_subvector_t; +} __attribute__ ((packed)) + +gds_subvector_t; typedef struct { _HWCB_HEADER -} __attribute__((packed)) read_hwcb_t; +} __attribute__ ((packed)) + +read_hwcb_t; -static read_hwcb_t read_hwcb_template = { +static read_hwcb_t read_hwcb_template = +{ PAGE_SIZE, 0x00, { @@ -225,4 +247,3 @@ static read_hwcb_t read_hwcb_template = { }; #endif /* __HWC_H__ */ - diff --git a/drivers/s390/char/hwc_con.c b/drivers/s390/char/hwc_con.c index 0ec9d9d5397a..f86c2c75c4a3 100644 --- a/drivers/s390/char/hwc_con.c +++ b/drivers/s390/char/hwc_con.c @@ -20,7 +20,6 @@ extern void hwc_tty_init(void); - #ifdef CONFIG_HWC_CONSOLE #define hwc_console_major 4 @@ -32,7 +31,9 @@ kdev_t hwc_console_device(struct console *); #define HWC_CON_PRINT_HEADER "hwc console driver: " -struct console hwc_console = { +struct console hwc_console = +{ + hwc_console_name, hwc_console_write, NULL, @@ -46,13 +47,15 @@ struct console hwc_console = { NULL }; -void hwc_console_write( +void +hwc_console_write ( struct console *console, const char *message, unsigned int count) { - if (console->device(console) != hwc_console.device(&hwc_console)) - { + + if (console->device (console) != hwc_console.device (&hwc_console)) { + hwc_printk(KERN_WARNING HWC_CON_PRINT_HEADER "hwc_console_write() called with wrong " "device number"); @@ -61,8 +64,8 @@ void hwc_console_write( hwc_write(0, message, count); } - -kdev_t hwc_console_device(struct console *c) +kdev_t +hwc_console_device (struct console * c) { return MKDEV(hwc_console_major, hwc_console_minor); } @@ -72,18 +75,23 @@ kdev_t hwc_console_device(struct console *c) __initfunc(unsigned long hwc_console_init(unsigned long kmem_start)) { - if (MACHINE_IS_P390) - return kmem_start; #ifdef CONFIG_3215 if (MACHINE_IS_VM) return kmem_start; #endif + if (MACHINE_IS_P390) + return kmem_start; + if (hwc_init(&kmem_start) == 0) { + #ifdef CONFIG_HWC_CONSOLE + register_console(&hwc_console); #endif + hwc_tty_init(); - } - else panic(HWC_CON_PRINT_HEADER "hwc initialisation failed !"); + } else + panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !"); + return kmem_start; } diff --git a/drivers/s390/char/hwc_rw.c b/drivers/s390/char/hwc_rw.c index 62d063025509..07d448e80745 100644 --- a/drivers/s390/char/hwc_rw.c +++ b/drivers/s390/char/hwc_rw.c @@ -32,14 +32,21 @@ #endif #define HWC_RW_PRINT_HEADER "hwc low level driver: " + #define USE_VM_DETECTION + #define DEFAULT_CASE_DELIMITER '%' #define DUMP_HWC_INIT_ERROR + #define DUMP_HWC_WRITE_ERROR + #define DUMP_HWC_WRITE_LIST_ERROR + #define DUMP_HWC_READ_ERROR + #undef DUMP_HWCB_INPUT + #undef BUFFER_STRESS_TEST typedef struct { @@ -49,9 +56,12 @@ typedef struct { unsigned char times_lost; unsigned short int mto_number_lost; unsigned long int mto_char_sum_lost; -} __attribute__((packed)) hwcb_list_t; +} __attribute__ ((packed)) + +hwcb_list_t; #define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t)) + #define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t)) #define BUF_HWCB hwc_data.hwcb_list_tail @@ -60,24 +70,40 @@ typedef struct { #define ALL_HWCB_CHAR hwc_data.mto_char_sum #define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)])) + #define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum) + #define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number) + #define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost) + #define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost) + #define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost) + #define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next) + #define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB) + #define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB) + #define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB) + #define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB) + #define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB) + #define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB) + #define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB) + #define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB) + #define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB) + #define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB) -#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB) +#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB) #include "hwc.h" @@ -93,36 +119,60 @@ static unsigned char typedef u32 kmem_pages_t; #define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3) + #define HWC_TIMER_RUNS 1 #define FLUSH_HWCBS 2 static struct { + hwc_ioctls_t ioctls; + hwc_ioctls_t init_ioctls; + unsigned char *hwcb_list_head; + unsigned char *hwcb_list_tail; + unsigned short int mto_number; + unsigned int mto_char_sum; + unsigned char hwcb_count; + unsigned long kmem_start; + unsigned long kmem_end; + kmem_pages_t kmem_pages; + unsigned char *obuf; + unsigned short int obuf_cursor; + unsigned short int obuf_count; + unsigned short int obuf_start; + unsigned char *page; + u32 current_servc; + unsigned char *current_hwcb; + unsigned char write_nonprio:1; unsigned char write_prio:1; unsigned char read_nonprio:1; unsigned char read_prio:1; + unsigned char flags; + spinlock_t lock; + struct timer_list write_timer; -} hwc_data = { - { }, +} hwc_data = +{ + { + }, { 8, 0, @@ -131,8 +181,11 @@ static struct { 1, 50, MAX_KMEM_PAGES, + 0, + 0x6c + }, NULL, NULL, @@ -154,6 +207,7 @@ static struct { 0, 0, 0 + }; #define DELAYED_WRITE 0 @@ -164,8 +218,8 @@ static signed int do_hwc_write(int from_user, const unsigned char *, unsigned char, unsigned char); - -static asmlinkage int internal_print(char write_time, const char *fmt, ...) +static asmlinkage int +internal_print (char write_time, const char *fmt,...) { va_list args; int i; @@ -177,8 +231,8 @@ static asmlinkage int internal_print(char write_time, const char *fmt, ...) return do_hwc_write(0, buf, i, CODE_ASCII, write_time); } - -int hwc_printk(const char *fmt, ...) +int +hwc_printk (const char *fmt,...) { va_list args; int i; @@ -199,7 +253,8 @@ int hwc_printk(const char *fmt, ...) #ifdef DUMP_HWCB_INPUT -static void dump_storage_area(unsigned char *area, unsigned short int count) +static void +dump_storage_area (unsigned char *area, unsigned short int count) { unsigned short int index; ioctl_nl_t old_final_nl; @@ -212,27 +267,28 @@ static void dump_storage_area(unsigned char *area, unsigned short int count) internal_print(DELAYED_WRITE, "\n%8x ", area); - for (index = 0; index < count; index++) { if (area[index] <= 0xF) internal_print(DELAYED_WRITE, "0%x", area[index]); - else internal_print(DELAYED_WRITE, "%x", area[index]); + else + internal_print (DELAYED_WRITE, "%x", area[index]); if ((index & 0xF) == 0xF) internal_print(DELAYED_WRITE, "\n%8x ", &area[index + 1]); else if ((index & 3) == 3) - internal_print(DELAYED_WRITE, " "); } internal_print(IMMEDIATE_WRITE, "\n"); + hwc_data.ioctls.final_nl = old_final_nl; } #endif -static inline u32 service_call( +static inline u32 +service_call ( u32 hwc_command_word, unsigned char hwcb[]) { @@ -248,12 +304,12 @@ static inline u32 service_call( __asm__ __volatile__("IPM %0 \n\t" "SRL %0, 28 \n\t" :"=r"(condition_code)); + return condition_code; } - - -static inline unsigned char * ext_int_param(void) +static inline unsigned char * +ext_int_param (void) { u32 param; @@ -263,7 +319,8 @@ static inline unsigned char * ext_int_param(void) return ((unsigned char *) param); } -static int prepare_write_hwcb(void) +static int +prepare_write_hwcb (void) { write_hwcb_t *hwcb; @@ -272,7 +329,9 @@ static int prepare_write_hwcb(void) BUF_HWCB_MTO = 0; BUF_HWCB_CHAR = 0; + hwcb = (write_hwcb_t *) BUF_HWCB; + memcpy(hwcb, &write_hwcb_template, sizeof(write_hwcb_t)); if (!hwc_data.write_nonprio && hwc_data.write_prio) @@ -281,7 +340,8 @@ static int prepare_write_hwcb(void) return 0; } -static int sane_write_hwcb(void) +static int +sane_write_hwcb (void) { unsigned short int lost_msg; unsigned int lost_char; @@ -294,7 +354,9 @@ static int sane_write_hwcb(void) return -ENOMEM; if ((unsigned long)OUT_HWCB & 0xFFF) { + bad_addr = OUT_HWCB; + #ifdef DUMP_HWC_WRITE_LIST_ERROR __asm__("LHI 1,0xe30\n\t" "LRA 2,0(0,%0) \n\t" @@ -306,6 +368,7 @@ static int sane_write_hwcb(void) hwc_data.kmem_pages = 0; if ((unsigned long)BUF_HWCB & 0xFFF) { + lost_hwcb = hwc_data.hwcb_count; lost_msg = ALL_HWCB_MTO; lost_char = ALL_HWCB_CHAR; @@ -316,6 +379,7 @@ static int sane_write_hwcb(void) ALL_HWCB_CHAR = 0; hwc_data.hwcb_count = 0; } else { + lost_hwcb = hwc_data.hwcb_count - 1; lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO; lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR; @@ -325,7 +389,6 @@ static int sane_write_hwcb(void) hwc_data.hwcb_count = 1; page = (unsigned long) BUF_HWCB; - if (page >= hwc_data.kmem_start && page < hwc_data.kmem_end) { @@ -350,7 +413,8 @@ static int sane_write_hwcb(void) return 0; } -static int reuse_write_hwcb(void) +static int +reuse_write_hwcb (void) { int retval; @@ -368,18 +432,28 @@ static int reuse_write_hwcb(void) #endif if (hwc_data.current_hwcb == OUT_HWCB) { + if (hwc_data.hwcb_count > 2) { + BUF_HWCB_NEXT = OUT_HWCB_NEXT; + BUF_HWCB = OUT_HWCB_NEXT; + OUT_HWCB_NEXT = BUF_HWCB_NEXT; + BUF_HWCB_NEXT = NULL; } } else { + BUF_HWCB_NEXT = OUT_HWCB; + BUF_HWCB = OUT_HWCB; + OUT_HWCB = OUT_HWCB_NEXT; + BUF_HWCB_NEXT = NULL; } + BUF_HWCB_TIMES_LOST += 1; BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR; BUF_HWCB_MTO_LOST += BUF_HWCB_MTO; @@ -401,7 +475,8 @@ static int reuse_write_hwcb(void) BUF_HWCB_TIMES_LOST, BUF_HWCB_MTO_LOST, BUF_HWCB_CHAR_LOST); - else internal_print( + else + internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "page allocation failed, " @@ -417,7 +492,8 @@ static int reuse_write_hwcb(void) return retval; } -static int allocate_write_hwcb(void) +static int +allocate_write_hwcb (void) { unsigned char *page; int page_nr; @@ -427,10 +503,13 @@ static int allocate_write_hwcb(void) page_nr = find_first_zero_bit(&hwc_data.kmem_pages, MAX_KMEM_PAGES); if (page_nr < hwc_data.ioctls.kmem_hwcb) { + page = (unsigned char *) (hwc_data.kmem_start + (page_nr << 12)); set_bit(page_nr, &hwc_data.kmem_pages); - } else page = (unsigned char *) __get_free_page(GFP_ATOMIC); + } else + page = (unsigned char *) __get_free_page (GFP_ATOMIC); + if (!page) return -ENOMEM; @@ -440,14 +519,19 @@ static int allocate_write_hwcb(void) BUF_HWCB_NEXT = page; BUF_HWCB = page; + BUF_HWCB_NEXT = NULL; + hwc_data.hwcb_count++; + prepare_write_hwcb(); + BUF_HWCB_TIMES_LOST = 0; BUF_HWCB_MTO_LOST = 0; BUF_HWCB_CHAR_LOST = 0; #ifdef BUFFER_STRESS_TEST + internal_print( DELAYED_WRITE, "*** " HWC_RW_PRINT_HEADER @@ -455,10 +539,12 @@ static int allocate_write_hwcb(void) hwc_data.hwcb_count, page); #endif + return 0; } -static int release_write_hwcb(void) +static int +release_write_hwcb (void) { unsigned long page; int page_nr; @@ -467,6 +553,7 @@ static int release_write_hwcb(void) return -ENODATA; if (hwc_data.hwcb_count == 1) { + prepare_write_hwcb(); ALL_HWCB_CHAR = 0; @@ -474,8 +561,7 @@ static int release_write_hwcb(void) BUF_HWCB_TIMES_LOST = 0; BUF_HWCB_MTO_LOST = 0; BUF_HWCB_CHAR_LOST = 0; - } - else { + } else { page = (unsigned long) OUT_HWCB; ALL_HWCB_MTO -= OUT_HWCB_MTO; @@ -486,13 +572,15 @@ static int release_write_hwcb(void) if (page >= hwc_data.kmem_start && page < hwc_data.kmem_end) { + memset((void *) page, 0, PAGE_SIZE); + page_nr = (int) ((page - hwc_data.kmem_start) >> 12); clear_bit(page_nr, &hwc_data.kmem_pages); - } - else + } else free_page(page); #ifdef BUFFER_STRESS_TEST + internal_print( DELAYED_WRITE, "*** " HWC_RW_PRINT_HEADER @@ -504,7 +592,8 @@ static int release_write_hwcb(void) return 0; } -static int add_mto( +static int +add_mto ( unsigned char *message, unsigned short int count) { @@ -520,15 +609,20 @@ static int add_mto( return -ENOMEM; mto_size = sizeof(mto_t) + count; + hwcb = (write_hwcb_t *) BUF_HWCB; if ((MAX_HWCB_ROOM - hwcb->length) < mto_size) return -ENOMEM; mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length); + memcpy(mto, &mto_template, sizeof(mto_t)); + dest = (void *) (((unsigned long) mto) + sizeof(mto_t)); + memcpy(dest, message, count); + mto->length += count; hwcb->length += mto_size; @@ -543,7 +637,8 @@ static int add_mto( return count; } -static int write_event_data_1(void) +static int +write_event_data_1 (void) { unsigned short int condition_code; int retval; @@ -557,9 +652,12 @@ static int write_event_data_1(void) retval = sane_write_hwcb(); if (retval < 0) return retval; + if (!OUT_HWCB_MTO) return -ENODATA; + condition_code = service_call(HWC_CMDW_WRITEDATA, OUT_HWCB); + #ifdef DUMP_HWC_WRITE_ERROR if (condition_code != HWC_COMMAND_INITIATED) __asm__("LHI 1,0xe20\n\t" @@ -570,6 +668,7 @@ static int write_event_data_1(void) :"a"(&condition_code), "a"(OUT_HWCB) :"1", "2", "3"); #endif + switch (condition_code) { case HWC_COMMAND_INITIATED : hwc_data.current_servc = HWC_CMDW_WRITEDATA; @@ -582,18 +681,23 @@ static int write_event_data_1(void) default : retval = -EIO; } + return retval; } -static void flush_hwcbs(void) +static void +flush_hwcbs (void) { while (hwc_data.hwcb_count > 1) release_write_hwcb(); + release_write_hwcb(); + hwc_data.flags &= ~FLUSH_HWCBS; } -static int write_event_data_2(void) +static int +write_event_data_2 (void) { write_hwcb_t *hwcb; int retval; @@ -659,11 +763,12 @@ static int write_event_data_2(void) :"1", "2", "3", "4", "5"); #endif - if (hwcb->response_code == 0x0020) { + retval = OUT_HWCB_CHAR; release_write_hwcb(); - } else retval = -EIO; + } else + retval = -EIO; hwc_data.current_servc = 0; hwc_data.current_hwcb = NULL; @@ -674,11 +779,14 @@ static int write_event_data_2(void) return retval; } -static void do_put_line( +static void +do_put_line ( unsigned char * message, unsigned short count) { + if (add_mto(message, count) != count) { + if (allocate_write_hwcb() < 0) reuse_write_hwcb(); @@ -700,7 +808,8 @@ static void do_put_line( } } -static void put_line( +static void +put_line ( unsigned char * message, unsigned short count) { @@ -709,15 +818,18 @@ static void put_line( del_timer(&hwc_data.write_timer); hwc_data.flags &= ~HWC_TIMER_RUNS; } - hwc_data.obuf_start += count; + do_put_line(message, count); + hwc_data.obuf_start -= count; } -static void set_alarm(void) +static void +set_alarm (void) { write_hwcb_t *hwcb; + if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb)) allocate_write_hwcb(); @@ -725,7 +837,8 @@ static void set_alarm(void) hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm; } -static void hwc_write_timeout(unsigned long data) +static void +hwc_write_timeout (unsigned long data) { unsigned long flags; @@ -735,14 +848,17 @@ static void hwc_write_timeout(unsigned long data) if (hwc_data.obuf_count) put_line(hwc_data.obuf, hwc_data.obuf_count); hwc_data.obuf_start = 0; + hwc_data.obuf_cursor = 0; hwc_data.obuf_count = 0; write_event_data_1(); + spin_unlock_irqrestore(&hwc_data.lock, flags); } -static int do_hwc_write( +static int +do_hwc_write ( int from_user, const unsigned char *msg, unsigned int count, @@ -769,27 +885,32 @@ static int do_hwc_write( } for (i_msg = 0; i_msg < count; i_msg++) { + if (from_user) get_user(orig_ch, msg + i_msg); else orig_ch = msg[i_msg]; if (code == CODE_EBCDIC) ch = _ebcasc[orig_ch]; - else ch = orig_ch; + else + ch = orig_ch; processed_characters++; if ((obuf_cursor == obuf_columns) && + (ch != '\n') && + (ch != '\t')) { put_line(&hwc_data.obuf[hwc_data.obuf_start], obuf_columns); obuf_cursor = 0; obuf_count = 0; } - switch (ch) { + case '\n' : + put_line(&hwc_data.obuf[hwc_data.obuf_start], obuf_count); obuf_cursor = 0; @@ -797,26 +918,30 @@ static int do_hwc_write( break; case '\a' : + hwc_data.obuf_start += obuf_count; set_alarm(); hwc_data.obuf_start -= obuf_count; + break; case '\t' : - do - { + + do { if (obuf_cursor < obuf_columns) { hwc_data.obuf[hwc_data.obuf_start + obuf_cursor] = 0x20; obuf_cursor++; - } - else break; + } else + break; } while (obuf_cursor % hwc_data.ioctls.width_htab); + break; case '\f' : case '\v' : + spaces = obuf_cursor; put_line(&hwc_data.obuf[hwc_data.obuf_start], obuf_count); @@ -827,18 +952,22 @@ static int do_hwc_write( = 0x20; spaces--; } + break; case '\b' : + if (obuf_cursor) obuf_cursor--; break; case '\r' : + obuf_cursor = 0; break; case 0x00 : + put_line(&hwc_data.obuf[hwc_data.obuf_start], obuf_count); obuf_cursor = 0; @@ -846,6 +975,7 @@ static int do_hwc_write( goto out; default: + if (isprint(ch)) hwc_data.obuf[hwc_data.obuf_start + obuf_cursor++] @@ -856,8 +986,8 @@ static int do_hwc_write( obuf_count = obuf_cursor; } - if (obuf_cursor) { + if (hwc_data.obuf_start || (hwc_data.ioctls.final_nl == 0)) { @@ -865,15 +995,17 @@ static int do_hwc_write( obuf_count); obuf_cursor = 0; obuf_count = 0; - } - else { + } else { + if (hwc_data.ioctls.final_nl > 0) { + if (hwc_data.flags & HWC_TIMER_RUNS) { + hwc_data.write_timer.expires = jiffies + hwc_data.ioctls.final_nl*HZ/10; - } - else { + } else { + init_timer(&hwc_data.write_timer); hwc_data.write_timer.function = hwc_write_timeout; @@ -885,49 +1017,47 @@ static int do_hwc_write( add_timer(&hwc_data.write_timer); hwc_data.flags |= HWC_TIMER_RUNS; } + } else; + } - else ; - } - } - else ; + } else; + out : + if (!hwc_data.obuf_start) { hwc_data.obuf_cursor = obuf_cursor; hwc_data.obuf_count = obuf_count; } - if (write_time == IMMEDIATE_WRITE) write_event_data_1(); return processed_characters; } - - - - -signed int hwc_write(int from_user, const unsigned char *msg, unsigned int count) +signed int +hwc_write (int from_user, const unsigned char *msg, unsigned int count) { unsigned long flags; int retval; spin_lock_irqsave(&hwc_data.lock, flags); + retval = do_hwc_write(from_user, msg, count, hwc_data.ioctls.code, IMMEDIATE_WRITE); + spin_unlock_irqrestore(&hwc_data.lock, flags); + return retval; } - - - - -unsigned int hwc_chars_in_buffer(unsigned char flag) +unsigned int +hwc_chars_in_buffer (unsigned char flag) { unsigned short int number = 0; unsigned long flags; spin_lock_irqsave(&hwc_data.lock, flags); + if (flag & IN_HWCB) number += ALL_HWCB_CHAR; @@ -935,10 +1065,12 @@ unsigned int hwc_chars_in_buffer(unsigned char flag) number += hwc_data.obuf_cursor; spin_unlock_irqrestore(&hwc_data.lock, flags); + return number; } -static inline int nr_setbits(kmem_pages_t arg) +static inline int +nr_setbits (kmem_pages_t arg) { int i; int nr = 0; @@ -948,17 +1080,21 @@ static inline int nr_setbits(kmem_pages_t arg) nr++; arg >>= 1; } + return nr; } -unsigned int hwc_write_room(unsigned char flag) +unsigned int +hwc_write_room (unsigned char flag) { unsigned int number = 0; unsigned long flags; write_hwcb_t *hwcb; spin_lock_irqsave(&hwc_data.lock, flags); + if (flag & IN_HWCB) { + if (BUF_HWCB) { hwcb = (write_hwcb_t *) BUF_HWCB; number += MAX_HWCB_ROOM - hwcb->length; @@ -970,18 +1106,24 @@ unsigned int hwc_write_room(unsigned char flag) } if (flag & IN_WRITE_BUF) number += MAX_HWCB_ROOM - hwc_data.obuf_cursor; + spin_unlock_irqrestore(&hwc_data.lock, flags); + return number; } -void hwc_flush_buffer(unsigned char flag) +void +hwc_flush_buffer (unsigned char flag) { unsigned long flags; + spin_lock_irqsave(&hwc_data.lock, flags); + if (flag & IN_HWCB) { if (hwc_data.current_servc != HWC_CMDW_WRITEDATA) flush_hwcbs(); - else hwc_data.flags |= FLUSH_HWCBS; + else + hwc_data.flags |= FLUSH_HWCBS; } if (flag & IN_WRITE_BUF) { hwc_data.obuf_cursor = 0; @@ -990,37 +1132,56 @@ void hwc_flush_buffer(unsigned char flag) spin_unlock_irqrestore(&hwc_data.lock, flags); } -unsigned short int seperate_cases(unsigned char *buf, unsigned short int count) +unsigned short int +seperate_cases (unsigned char *buf, unsigned short int count) { + unsigned short int i_in; + unsigned short int i_out = 0; + unsigned char _case = 0; for (i_in = 0; i_in < count; i_in++) { + if (buf[i_in] == hwc_data.ioctls.delim) { + if ((i_in + 1 < count) && (buf[i_in + 1] == hwc_data.ioctls.delim)) { + buf[i_out] = hwc_data.ioctls.delim; + i_out++; + i_in++; - } else _case = ~_case; + + } else + _case = ~_case; + } else { + if (_case) { + if (hwc_data.ioctls.tolower) buf[i_out] = _ebc_toupper[buf[i_in]]; - else buf[i_out] = _ebc_tolower[buf[i_in]]; - } else buf[i_out] = buf[i_in]; + + else + buf[i_out] = _ebc_tolower[buf[i_in]]; + + } else + buf[i_out] = buf[i_in]; + i_out++; } } + return i_out; } - - #ifdef DUMP_HWCB_INPUT -static int gds_vector_name(u16 id, unsigned char name[]) +static int +gds_vector_name (u16 id, unsigned char name[]) { int retval = 0; @@ -1059,9 +1220,8 @@ static int gds_vector_name(u16 id, unsigned char name[]) } #endif - - -inline static gds_vector_t * find_gds_vector( +inline static gds_vector_t * +find_gds_vector ( gds_vector_t *start, void *end, u16 id) { gds_vector_t *vec; @@ -1090,22 +1250,23 @@ inline static gds_vector_t * find_gds_vector( IMMEDIATE_WRITE, ", id: 0x%x\n", vec->gds_id); - else internal_print( + else + internal_print ( IMMEDIATE_WRITE, "\n"); #endif + retval = vec; break; } - vec = (gds_vector_t *) (((unsigned long) vec) + vec->length); } return retval; } - -inline static gds_subvector_t * find_gds_subvector( +inline static gds_subvector_t * +find_gds_subvector ( gds_subvector_t *start, void *end, u8 key) { gds_subvector_t *subvec; @@ -1121,11 +1282,12 @@ inline static gds_subvector_t * find_gds_subvector( subvec = (gds_subvector_t *) (((unsigned long) subvec) + subvec->length); } + return retval; } - -inline static int get_input(void *start, void *end) +inline static int +get_input (void *start, void *end) { int count; @@ -1133,18 +1295,23 @@ inline static int get_input(void *start, void *end) if (hwc_data.ioctls.tolower) EBC_TOLOWER(start, count); + if (hwc_data.ioctls.delim) count = seperate_cases(start, count); + if (hwc_data.ioctls.echo) do_hwc_write(0, start, count, CODE_EBCDIC, IMMEDIATE_WRITE); + if (hwc_data.ioctls.code == CODE_ASCII) EBCASC(start, count); + store_hwc_input(start, count); + return count; } - -inline static int eval_selfdeftextmsg(gds_subvector_t *start, void *end) +inline static int +eval_selfdeftextmsg (gds_subvector_t * start, void *end) { gds_subvector_t *subvec; void *subvec_data; @@ -1165,11 +1332,12 @@ inline static int eval_selfdeftextmsg(gds_subvector_t *start, void *end) retval += get_input(subvec_data, subvec_end); subvec = (gds_subvector_t *) subvec_end; } + return retval; } - -inline static int eval_textcmd(gds_subvector_t *start, void *end) +inline static int +eval_textcmd (gds_subvector_t * start, void *end) { gds_subvector_t *subvec; gds_subvector_t *subvec_data; @@ -1195,8 +1363,8 @@ inline static int eval_textcmd(gds_subvector_t *start, void *end) return retval; } - -inline static int eval_cpmsu(gds_vector_t *start, void *end) +inline static int +eval_cpmsu (gds_vector_t * start, void *end) { gds_vector_t *vec; gds_subvector_t *vec_data; @@ -1219,8 +1387,8 @@ inline static int eval_cpmsu(gds_vector_t *start, void *end) return retval; } - -inline static int eval_mdsmu(gds_vector_t *start, void *end) +inline static int +eval_mdsmu (gds_vector_t * start, void *end) { gds_vector_t *vec; gds_vector_t *vec_data; @@ -1234,12 +1402,11 @@ inline static int eval_mdsmu(gds_vector_t *start, void *end) vec_end = (void *) (((unsigned long) vec) + vec->length); retval = eval_cpmsu(vec_data, vec_end); } - return retval; } - -inline static int eval_evbuf(gds_vector_t *start, void *end) +inline static int +eval_evbuf (gds_vector_t * start, void *end) { gds_vector_t *vec; gds_vector_t *vec_data; @@ -1253,12 +1420,11 @@ inline static int eval_evbuf(gds_vector_t *start, void *end) vec_end = (void *) (((unsigned long) vec) + vec->length); retval = eval_mdsmu(vec_data, vec_end); } - return retval; } - -static int process_evbufs(void *start, void *end) +static int +process_evbufs (void *start, void *end) { int retval = 0; evbuf_t *evbuf; @@ -1307,7 +1473,8 @@ static int process_evbufs(void *start, void *end) return retval; } -static int unconditional_read_1(void) +static int +unconditional_read_1 (void) { unsigned short int condition_code; read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page; @@ -1315,6 +1482,7 @@ static int unconditional_read_1(void) if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio)) return -EOPNOTSUPP; + if (hwc_data.current_servc) return -EBUSY; @@ -1350,7 +1518,8 @@ static int unconditional_read_1(void) return retval; } -static int unconditional_read_2(void) +static int +unconditional_read_2 (void) { read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page; @@ -1372,14 +1541,17 @@ static int unconditional_read_2(void) hwc_data.current_hwcb = NULL; switch (hwcb->response_code) { + case 0x0020 : case 0x0220 : return process_evbufs( (void *) (((unsigned long) hwcb) + sizeof(read_hwcb_t)), (void *) (((unsigned long) hwcb) + hwcb->length)); + case 0x60F0 : case 0x62F0 : return 0; + case 0x0100 : internal_print( IMMEDIATE_WRITE, @@ -1445,14 +1617,18 @@ static int unconditional_read_2(void) } } -static int write_event_mask_1(void) +static int +write_event_mask_1 (void) { unsigned int condition_code; int retval; memcpy(hwc_data.page, &init_hwcb_template, sizeof(init_hwcb_t)); + condition_code = service_call(HWC_CMDW_WRITEMASK, hwc_data.page); + #ifdef DUMP_HWC_INIT_ERROR + if (condition_code != HWC_COMMAND_INITIATED) __asm__("LHI 1,0xe10\n\t" "L 2,0(0,%0)\n\t" @@ -1475,19 +1651,16 @@ static int write_event_mask_1(void) default : retval = -EIO; } + return retval; } - - - - -static int write_event_mask_2(void) +static int +write_event_mask_2 (void) { init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page; int retval = 0; - if (hwcb->hwc_receive_mask & ET_Msg_Mask) hwc_data.write_nonprio = 1; @@ -1521,7 +1694,8 @@ static int write_event_mask_2(void) return retval; } -static int set_hwc_ioctls(hwc_ioctls_t *ioctls, char correct) +static int +set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct) { int retval = 0; hwc_ioctls_t tmp; @@ -1529,17 +1703,20 @@ static int set_hwc_ioctls(hwc_ioctls_t *ioctls, char correct) if (ioctls->width_htab > MAX_MESSAGE_SIZE) { if (correct) tmp.width_htab = MAX_MESSAGE_SIZE; - else retval = -EINVAL; - } else tmp.width_htab = ioctls->width_htab; + else + retval = -EINVAL; + } else + tmp.width_htab = ioctls->width_htab; tmp.echo = ioctls->echo; if (ioctls->columns > MAX_MESSAGE_SIZE) { if (correct) tmp.columns = MAX_MESSAGE_SIZE; - else retval = -EINVAL; - } else tmp.columns = ioctls->columns; - + else + retval = -EINVAL; + } else + tmp.columns = ioctls->columns; switch (ioctls->code) { case CODE_EBCDIC : @@ -1549,7 +1726,8 @@ static int set_hwc_ioctls(hwc_ioctls_t *ioctls, char correct) default : if (correct) tmp.code = CODE_ASCII; - else retval = -EINVAL; + else + retval = -EINVAL; } tmp.final_nl = ioctls->final_nl; @@ -1557,40 +1735,43 @@ static int set_hwc_ioctls(hwc_ioctls_t *ioctls, char correct) if (ioctls->max_hwcb < 2) { if (correct) tmp.max_hwcb = 2; - else retval = -EINVAL; - } else tmp.max_hwcb = ioctls->max_hwcb; + else + retval = -EINVAL; + } else + tmp.max_hwcb = ioctls->max_hwcb; tmp.tolower = ioctls->tolower; if (ioctls->kmem_hwcb > ioctls->max_hwcb) { if (correct) tmp.kmem_hwcb = ioctls->max_hwcb; - else retval = -EINVAL; - } else tmp.kmem_hwcb = ioctls->kmem_hwcb; - + else + retval = -EINVAL; + } else + tmp.kmem_hwcb = ioctls->kmem_hwcb; if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) { if (correct) ioctls->kmem_hwcb = MAX_KMEM_PAGES; - else retval = -EINVAL; + else + retval = -EINVAL; } - if (ioctls->kmem_hwcb < 2) { if (correct) ioctls->kmem_hwcb = 2; - else retval = -EINVAL; + else + retval = -EINVAL; } - tmp.delim = ioctls->delim; - if (!(retval < 0)) hwc_data.ioctls = tmp; return retval; } -int hwc_init(unsigned long *kmem_start) +int +hwc_init (unsigned long *kmem_start) { int retval; #ifdef BUFFER_STRESS_TEST @@ -1599,16 +1780,22 @@ int hwc_init(unsigned long *kmem_start) int i; #endif + #ifdef CONFIG_3215 if (MACHINE_IS_VM) return kmem_start; #endif + spin_lock_init(&hwc_data.lock); + retval = write_event_mask_1(); if (retval < 0) return retval; + #ifdef USE_VM_DETECTION + if (MACHINE_IS_VM) { + if (hwc_data.init_ioctls.columns > 76) hwc_data.init_ioctls.columns = 76; hwc_data.init_ioctls.tolower = 1; @@ -1627,7 +1814,9 @@ int hwc_init(unsigned long *kmem_start) hwc_data.kmem_end = *kmem_start - 1; ctl_set_bit(0, 9); + #ifdef BUFFER_STRESS_TEST + internal_print( DELAYED_WRITE, HWC_RW_PRINT_HEADER @@ -1644,36 +1833,50 @@ int hwc_init(unsigned long *kmem_start) } #endif + return retval; } -void do_hwc_interrupt(void) +void +do_hwc_interrupt (void) { + spin_lock(&hwc_data.lock); + if (!hwc_data.current_servc) { + unconditional_read_1(); + } else { + switch (hwc_data.current_servc) { + case HWC_CMDW_WRITEMASK : + write_event_mask_2(); break; case HWC_CMDW_WRITEDATA : + write_event_data_2(); break; case HWC_CMDW_READDATA : + unconditional_read_2(); break; } + write_event_data_1(); } + wake_up_hwc_tty(); + spin_unlock(&hwc_data.lock); } - -int hwc_ioctl(unsigned int cmd, unsigned long arg) +int +hwc_ioctl (unsigned int cmd, unsigned long arg) { hwc_ioctls_t tmp = hwc_data.ioctls; int retval = 0; @@ -1683,6 +1886,7 @@ int hwc_ioctl(unsigned int cmd, unsigned long arg) spin_lock_irqsave(&hwc_data.lock, flags); switch (cmd) { + case TIOCHWCSHTAB : if (get_user(tmp.width_htab, (ioctl_htab_t *) arg)) goto fault; @@ -1714,10 +1918,10 @@ int hwc_ioctl(unsigned int cmd, unsigned long arg) goto fault; if (obuf & 0xFFF) tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12); - else tmp.max_hwcb = (obuf >> 12); + else + tmp.max_hwcb = (obuf >> 12); break; - case TIOCHWCSCASE : if (get_user(tmp.tolower, (ioctl_case_t *) arg)) goto fault; @@ -1728,55 +1932,46 @@ int hwc_ioctl(unsigned int cmd, unsigned long arg) goto fault; break; - case TIOCHWCSINIT : retval = set_hwc_ioctls(&hwc_data.init_ioctls, 1); break; - case TIOCHWCGHTAB : if (put_user(tmp.width_htab, (ioctl_htab_t *) arg)) goto fault; break; - case TIOCHWCGECHO : if (put_user(tmp.echo, (ioctl_echo_t *) arg)) goto fault; break; - case TIOCHWCGCOLS : if (put_user(tmp.columns, (ioctl_cols_t *) arg)) goto fault; break; - case TIOCHWCGCODE : if (put_user(tmp.code, (ioctl_code_t *) arg)) goto fault; break; - case TIOCHWCGNL : if (put_user(tmp.final_nl, (ioctl_nl_t *) arg)) goto fault; break; - case TIOCHWCGOBUF : if (put_user(tmp.max_hwcb, (ioctl_obuf_t *) arg)) goto fault; break; - case TIOCHWCGKBUF : if (put_user(tmp.kmem_hwcb, (ioctl_obuf_t *) arg)) goto fault; break; - case TIOCHWCGCASE : if (put_user(tmp.tolower, (ioctl_case_t *) arg)) goto fault; @@ -1793,7 +1988,6 @@ int hwc_ioctl(unsigned int cmd, unsigned long arg) goto fault; break; - case TIOCHWCGCURR : if (put_user(&hwc_data.ioctls, (hwc_ioctls_t *) arg)) goto fault; @@ -1804,7 +1998,6 @@ int hwc_ioctl(unsigned int cmd, unsigned long arg) goto noioctlcmd; } - if (_IOC_DIR(cmd) == _IOC_WRITE) retval = set_hwc_ioctls(&tmp, 0); diff --git a/drivers/s390/char/hwc_rw.h b/drivers/s390/char/hwc_rw.h index a3b072b008bd..ab0ac48b6986 100644 --- a/drivers/s390/char/hwc_rw.h +++ b/drivers/s390/char/hwc_rw.h @@ -10,115 +10,38 @@ #ifndef __HWC_RW_H__ #define __HWC_RW_H__ - #include - - - - - - #ifndef __HWC_RW_C__ - - - - - - extern int hwc_init(unsigned long *); - - - - - extern int hwc_write(int from_user, const unsigned char *, unsigned int); - - - - - - extern unsigned int hwc_chars_in_buffer(unsigned char); - - - - - - extern unsigned int hwc_write_room(unsigned char); - - - - - - extern void hwc_flush_buffer(unsigned char); - - - - - extern signed int hwc_ioctl(unsigned int, unsigned long); - - - - - - - - extern void do_hwc_interrupt(void); - - - - - extern int hwc_printk(const char *, ...); #else - - - - extern void store_hwc_input(unsigned char*, unsigned int); - - - extern void wake_up_hwc_tty(void); #endif - - - - - - - - - - - - - - #define IN_HWCB 1 #define IN_WRITE_BUF 2 #define IN_BUFS_TOTAL (IN_HWCB | IN_WRITE_BUF) - - typedef unsigned short int ioctl_htab_t; typedef unsigned char ioctl_echo_t; typedef unsigned short int ioctl_cols_t; @@ -128,7 +51,6 @@ typedef unsigned short int ioctl_obuf_t; typedef unsigned char ioctl_case_t; typedef unsigned char ioctl_delim_t; - typedef struct { ioctl_htab_t width_htab; ioctl_echo_t echo; @@ -141,22 +63,10 @@ typedef struct { ioctl_delim_t delim; } hwc_ioctls_t; - - - - - static hwc_ioctls_t _hwc_ioctls; - - - - - - #define HWC_IOCTL_LETTER 'B' - #define TIOCHWCSHTAB _IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab) #define TIOCHWCSECHO _IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo) @@ -173,10 +83,8 @@ static hwc_ioctls_t _hwc_ioctls; #define TIOCHWCSCASE _IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower) - #define TIOCHWCSDELIM _IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim) - #define TIOCHWCGHTAB _IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab) #define TIOCHWCGECHO _IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo) @@ -193,18 +101,13 @@ static hwc_ioctls_t _hwc_ioctls; #define TIOCHWCGCASE _IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower) - #define TIOCHWCGDELIM _IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim) #define TIOCHWCGKBUF _IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb) #define TIOCHWCGCURR _IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls) - - - #define CODE_ASCII 0x0 #define CODE_EBCDIC 0x1 - #endif diff --git a/drivers/s390/char/hwc_tty.c b/drivers/s390/char/hwc_tty.c index ab212da0d192..bfed602ad473 100644 --- a/drivers/s390/char/hwc_tty.c +++ b/drivers/s390/char/hwc_tty.c @@ -21,12 +21,17 @@ #include "hwc_rw.h" #define HWC_TTY_PRINT_HEADER "hwc tty driver: " + #define HWC_TTY_BUF_SIZE 512 typedef struct { + struct tty_struct *tty; + unsigned char buf[HWC_TTY_BUF_SIZE]; + unsigned short int buf_count; + spinlock_t lock; } hwc_tty_data_struct; @@ -39,9 +44,11 @@ static int hwc_tty_refcount = 0; extern struct termios tty_std_termios; -static int hwc_tty_open(struct tty_struct *tty, +static int +hwc_tty_open (struct tty_struct *tty, struct file *filp) { + if (MINOR(tty->device) - tty->driver.minor_start) return -ENODEV; @@ -53,7 +60,8 @@ static int hwc_tty_open(struct tty_struct *tty, return 0; } -void wake_up_hwc_tty(void) +void +wake_up_hwc_tty (void) { if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && hwc_tty_data.tty->ldisc.write_wakeup) @@ -61,7 +69,8 @@ void wake_up_hwc_tty(void) wake_up_interruptible(&hwc_tty_data.tty->write_wait); } -static void hwc_tty_close(struct tty_struct *tty, +static void +hwc_tty_close (struct tty_struct *tty, struct file *filp) { if (MINOR(tty->device) != tty->driver.minor_start) { @@ -69,11 +78,11 @@ static void hwc_tty_close(struct tty_struct *tty, "do not close hwc tty because of wrong device number"); return; } - hwc_tty_data.tty = NULL; } -static int hwc_tty_write_room (struct tty_struct *tty) +static int +hwc_tty_write_room (struct tty_struct *tty) { int retval; @@ -81,15 +90,15 @@ static int hwc_tty_write_room (struct tty_struct *tty) return retval; } -static int hwc_tty_write(struct tty_struct *tty, +static int +hwc_tty_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count) { int retval; - if (hwc_tty_data.buf_count > 0) - { + if (hwc_tty_data.buf_count > 0) { hwc_write(0, hwc_tty_data.buf, hwc_tty_data.buf_count); hwc_tty_data.buf_count = 0; } @@ -97,14 +106,14 @@ static int hwc_tty_write(struct tty_struct *tty, return retval; } -static void hwc_tty_put_char(struct tty_struct *tty, +static void +hwc_tty_put_char (struct tty_struct *tty, unsigned char ch) { unsigned long flags; spin_lock_irqsave(&hwc_tty_data.lock, flags); - if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) - { + if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) { hwc_write(0, hwc_tty_data.buf, hwc_tty_data.buf_count); hwc_tty_data.buf_count = 0; } @@ -113,7 +122,8 @@ static void hwc_tty_put_char(struct tty_struct *tty, spin_unlock_irqrestore(&hwc_tty_data.lock, flags); } -static void hwc_tty_flush_chars(struct tty_struct *tty) +static void +hwc_tty_flush_chars (struct tty_struct *tty) { unsigned long flags; @@ -123,8 +133,8 @@ static void hwc_tty_flush_chars(struct tty_struct *tty) spin_unlock_irqrestore(&hwc_tty_data.lock, flags); } - -static int hwc_tty_chars_in_buffer(struct tty_struct *tty) +static int +hwc_tty_chars_in_buffer (struct tty_struct *tty) { int retval; @@ -132,12 +142,14 @@ static int hwc_tty_chars_in_buffer(struct tty_struct *tty) return retval; } -static void hwc_tty_flush_buffer(struct tty_struct *tty) +static void +hwc_tty_flush_buffer (struct tty_struct *tty) { wake_up_hwc_tty(); } -static int hwc_tty_ioctl( +static int +hwc_tty_ioctl ( struct tty_struct *tty, struct file * file, unsigned int cmd, @@ -149,11 +161,13 @@ static int hwc_tty_ioctl( return hwc_ioctl(cmd, arg); } -void store_hwc_input(unsigned char* buf, unsigned int count) +void +store_hwc_input (unsigned char *buf, unsigned int count) { struct tty_struct *tty = hwc_tty_data.tty; if (tty != NULL) { + if (count == 2 && strncmp(buf, "^c", 2) == 0) { tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; @@ -167,12 +181,14 @@ void store_hwc_input(unsigned char* buf, unsigned int count) *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = SUSP_CHAR(tty); } else { + memcpy(tty->flip.char_buf_ptr, buf, count); if (count < 2 || strncmp(buf + count - 2, "^n", 2)) { tty->flip.char_buf_ptr[count] = '\n'; count++; - } else count -= 2; + } else + count -= 2; memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count); tty->flip.char_buf_ptr += count; tty->flip.flag_buf_ptr += count; @@ -183,7 +199,8 @@ void store_hwc_input(unsigned char* buf, unsigned int count) } } -void hwc_tty_init(void) +void +hwc_tty_init (void) { memset (&hwc_tty_driver, 0, sizeof(struct tty_driver)); hwc_tty_driver.magic = TTY_DRIVER_MAGIC; diff --git a/drivers/s390/net/ctc.c b/drivers/s390/net/ctc.c index 23dc047e81c6..47477fd088fa 100644 --- a/drivers/s390/net/ctc.c +++ b/drivers/s390/net/ctc.c @@ -3,8 +3,42 @@ * CTC / ESCON network driver * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Dieter Wellerdiek (wel@de.ibm.com) + * + * + * Description of the Kernel Parameter + * Normally the CTC driver selects the channels in order (automatic channel + * selection). If your installation needs to use the channels in a different + * order or doesn't want to have automatic channel selection on, you can do + * this with the "ctc= kernel keyword". + * + * ctc=0,0xrrrr,0xwwww,ddddd + * + * Where: + * + * "rrrr" is the read channel address + * "wwww" is the write channel address + * "dddd" is the network device (ctc0 to ctc7 for a parallel channel, escon0 + * to escon7 for ESCON channels). + * + * To switch the automatic channel selection off use the ctc= keyword with + * parameter "noauto". This may be necessary if you 3271 devices or other devices + * which use the ctc device type and model, but operate with a different protocol. + * + * ctc=noauto + * + * Change History + * 0.50 Initial release shipped + * 0.51 Bug fixes + * - CTC / ESCON network device can now handle up to 64 channels + * - 3088-61 info message supperssed - CISCO 7206 - CLAW - ESCON + * - 3088-62 info message suppressed - OSA/D + * - channel: def ffffffed ... error message suppressed + * - CTC / ESCON device was not recoverable after a lost connection with + * IFCONFIG dev DOWN and IFCONFIG dev UP + * - Possibility to switch the automatic selection off + * - Minor bug fixes */ #include @@ -28,12 +62,12 @@ #include #include -#include "../../../arch/s390/kernel/irq.h" +#include //#define DEBUG -/* Redefine message level, so that all messages occure on 3215 console in DEBUG mode */ +/* Redefine message level, so that all messages occur on 3215 console in DEBUG mode */ #ifdef DEBUG #undef KERN_INFO #undef KERN_WARNING @@ -49,8 +83,8 @@ #define CCW_CMD_SET_EXTENDED 0xc3 #define CCW_CMD_PREPARE 0xe3 -#define MAX_DEVICES 16 -#define MAX_ADAPTERS MAX_DEVICES / 2 +#define MAX_CHANNEL_DEVICES 64 +#define MAX_ADAPTERS 8 #define CTC_DEFAULT_MTU_SIZE 1500 #define READ 0 #define WRITE 1 @@ -77,7 +111,7 @@ typedef enum { channel_type_none, /* Device is not a channel */ - channel_type_undefined, /* Device is a channel but we dont know anything about it */ + channel_type_undefined, /* Device is a channel but we don't know anything about it */ channel_type_ctca, /* Device is a CTC/A and we can deal with it */ channel_type_escon, /* Device is a ESCON channel and we can deal with it */ channel_type_unsupported /* Device is a unsupported model */ @@ -99,7 +133,7 @@ struct devicelist { }; static struct { - struct devicelist list[MAX_DEVICES]; + struct devicelist list[MAX_CHANNEL_DEVICES]; int count; int left; } channel[CHANNEL_MEDIA]; @@ -113,7 +147,7 @@ struct adapterlist{ __u16 protocol; }; -static struct adapterlist ctc_adapter[CHANNEL_MEDIA][MAX_ADAPTERS]; +static struct adapterlist ctc_adapter[CHANNEL_MEDIA][MAX_ADAPTERS]; /* 0 = CTC / 1 = ESCON */ /* @@ -131,7 +165,7 @@ struct buffer { struct channel { unsigned int devno; int irq; - int IO_active; + unsigned long IO_active; ccw1_t ccw[3]; __u32 state; int buffer_count; @@ -152,7 +186,7 @@ struct channel { struct ctc_priv { - struct net_device_stats stats; + struct enet_statistics stats; struct channel channel[2]; __u16 protocol; }; @@ -224,7 +258,7 @@ static void channel_init(void) if (!test_and_set_bit(0, (void *)& channel_tab_initialized)){ channel_scan(); for (m = 0; m < CHANNEL_MEDIA; m++) { - channel_sort (channel[m].list, MAX_DEVICES); + channel_sort (channel[m].list, MAX_CHANNEL_DEVICES); channel[m].left = channel[m].count; } if (channel[CTC].count == 0 && channel[ESCON].count == 0) @@ -234,7 +268,7 @@ static void channel_init(void) channel[CTC].count, channel[ESCON].count); #ifdef DEBUG for (m = 0; m < CHANNEL_MEDIA; m++) { - for (c = 0; c < MAX_DEVICES; c++){ + for (c = 0; c < MAX_CHANNEL_DEVICES; c++){ printk(KERN_DEBUG "channel: Adapter=%x Entry=%x devno=%04x\n", m, c, channel[m].list[c].devno); } @@ -255,14 +289,14 @@ static void channel_scan(void) dev_info_t temp; for (m = 0; m < CHANNEL_MEDIA; m++) { - for (c = 0; c < MAX_DEVICES; c++){ + for (c = 0; c < MAX_CHANNEL_DEVICES; c++){ channel[m].list[c].devno = -ENODEV; } } for (irq = 0; irq < NR_IRQS; irq++) { /* CTC/A */ - if (channel[CTC].count < MAX_DEVICES ) { + if (channel[CTC].count < MAX_CHANNEL_DEVICES ) { if (get_dev_info(irq, &temp) == 0 && channel_check_for_type(&temp.sid_data) == channel_type_ctca) { channel[CTC].list[channel[CTC].count].devno = temp.devno; @@ -271,7 +305,7 @@ static void channel_scan(void) } /* ESCON */ - if (channel[ESCON].count < MAX_DEVICES ) { + if (channel[ESCON].count < MAX_CHANNEL_DEVICES ) { if (get_dev_info(irq, &temp) == 0 && channel_check_for_type(&temp.sid_data) == channel_type_escon) { channel[ESCON].list[channel[ESCON].count].devno = temp.devno; @@ -363,21 +397,23 @@ static channel_type_t channel_check_for_type (senseid_t *id) switch (id->cu_model) { case 0x08: - type = channel_type_ctca; /* 3088/08 ==> CTCA */ + type = channel_type_ctca; /* 3088-08 ==> CTCA */ break; case 0x1F: - type = channel_type_escon; /* 3088/1F ==> ESCON channel */ + type = channel_type_escon; /* 3088-1F ==> ESCON channel */ break; - case 0x01: /* 3088/01 ==> P390 OSA emulation */ - case 0x60: /* 3088/60 ==> OSA/2 Adapter */ + case 0x01: /* 3088-01 ==> P390 OSA emulation */ + case 0x60: /* 3088-60 ==> OSA/2 adapter */ + case 0x61: /* 3088-61 ==> CISCO 7206 CLAW protocol ESCON connected */ + case 0x62: /* 3088-62 ==> OSA/D device */ type = channel_type_unsupported; break; default: type = channel_type_undefined; - printk(KERN_INFO "channel: Unknown model found 3088/%02x\n",id->cu_model); + printk(KERN_INFO "channel: Unknown model found 3088-%02x\n",id->cu_model); } break; @@ -545,6 +581,11 @@ void ctc_setup(char *dev_name, int *ints) ctc_no_auto = 1; + if (!strcmp(dev_name,"noauto")) { + printk(KERN_INFO "ctc: automatic channel selection deactivated\n"); + return; + } + tmp.devno[WRITE] = -ENODEV; tmp.devno[READ] = -ENODEV; @@ -556,7 +597,7 @@ void ctc_setup(char *dev_name, int *ints) case 2: /* read channel passed */ tmp.devno[READ] = ints[2]; if (tmp.devno[WRITE] == -ENODEV) - tmp.devno[WRITE] = tmp.devno[READ]++; + tmp.devno[WRITE] = tmp.devno[READ] + 1; case 1: /* protocol type passed */ tmp.protocol = ints[1]; @@ -566,9 +607,10 @@ void ctc_setup(char *dev_name, int *ints) printk(KERN_WARNING "%s: wrong Channel protocol type passed\n", dev_name); return; } + break; default: - printk(KERN_WARNING "%s: wrong number of parameter passed\n", dev_name); + printk(KERN_WARNING "ctc: wrong number of parameter passed\n"); return; } ctc_adapter[extract_channel_media(dev_name)][extract_channel_id(dev_name)] = tmp; @@ -610,6 +652,9 @@ int ctc_probe(struct device *dev) if (channel_left(m) <=1) return -ENODEV; + if (ctc_no_auto == 1 && (ctc_adapter[m][i].devno[READ] == -ENODEV || ctc_adapter[m][i].devno[WRITE] == -ENODEV)) + return -ENODEV; + dev->priv = kmalloc(sizeof(struct ctc_priv), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; @@ -618,6 +663,7 @@ int ctc_probe(struct device *dev) for (c = 0; c < 2; c++) { + privptr->channel[c].devstat = kmalloc(sizeof(devstat_t), GFP_KERNEL); if (privptr->channel[c].devstat == NULL){ if (i == WRITE) @@ -1031,7 +1077,7 @@ static void ctc_irq_bh (struct channel *ctc) skb->mac.raw = skb->data; skb->dev = dev; skb->protocol = htons(ETH_P_IP); - skb->ip_summed = CHECKSUM_UNNECESSARY; /* no UC happend!!! */ + skb->ip_summed = CHECKSUM_UNNECESSARY; /* no UC happened!!! */ netif_rx(skb); privptr->stats.rx_packets++; } else { @@ -1150,6 +1196,7 @@ static int ctc_open(struct device *dev) privptr->channel[i].dev = dev; privptr->channel[i].flag_a = 0; + privptr->channel[i].IO_active = 0; privptr->channel[i].ccw[0].cmd_code = CCW_CMD_PREPARE; privptr->channel[i].ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC; diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c index 72e0cf996637..ba938e4392e4 100644 --- a/drivers/s390/net/iucv.c +++ b/drivers/s390/net/iucv.c @@ -3,7 +3,7 @@ * Network driver for VM using iucv * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Stefan Hegewald * Hartmut Penner */ diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 4ff28b30b438..4b5ca3a1907e 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -278,6 +278,7 @@ static struct dev_info device_list[] = {"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"YAMAHA","CRW8424S","1.0", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ +{"YAMAHA","CRW6416S","1.0c", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN */ /* @@ -3456,6 +3457,8 @@ scsi_dump_status(int level) #ifdef MODULE +MODULE_PARM(max_scsi_luns, "i"); + int init_module(void) { unsigned long size; diff --git a/drivers/sound/cmpci.c b/drivers/sound/cmpci.c index 208cac6474f1..0bb2464c58d4 100644 --- a/drivers/sound/cmpci.c +++ b/drivers/sound/cmpci.c @@ -21,7 +21,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * Special thanks to David C. Niemi + * Special thanks to David C. Niemi, Jan Pfeifer * * * Module command line parameters: @@ -57,9 +57,45 @@ * reported by Johan Maes * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK * read/write cannot be executed + * 20 09 99 0.13 merged the generic changes in sonicvibes since this + * diverged. + * 18.08.99 1.5 Only deallocate DMA buffer when unloading. + * 02.09.99 1.6 Enable SPDIF LOOP + * Change the mixer read back + * 21.09.99 2.33 Use RCS version aas driver version. + * Add support for modem, S/PDIF loop and 4 channels. + * (8738 only) + * Fix bug cause x11amp cannot play. + * $Log: cmpci.c,v $ + * Revision 2.41 1999/10/27 02:00:05 cltien + * Now the fragsize for modem is activated by parameter. + * + * Revision 2.40 1999/10/26 23:38:26 cltien + * Remove debugging message in cm_write which may cause module counter not 0. + * + * Revision 2.39 1999/10/26 21:52:50 cltien + * I forgor too adjust mic recording volume, as it should be moved to 5MUTEMONO. + * Change the DYNAMIC macro to FIXEDDMA, which means static DMA buffer. + * + * Revision 2.38 1999/10/08 21:59:03 cltien + * Set FLINKON and reset FLINKOFF for modem. + * + * Revision 2.37 1999/09/28 02:57:04 cltien + * Add set_bus_master() to make sure bus master enabled. + * + * Revision 2.36 1999/09/22 14:15:03 cltien + * Use open_sem to avoid multiple access to open_mode. + * Use wakeup in IntrClose to activate process in waiting queue. + * + * Revision 2.35 1999/09/22 13:20:53 cltien + * Use open_mode to check if DAC in used. Also more check in IntrWrite and IntrClose. Now the modem can access DAC safely. + * + * Revision 2.34 1999/09/22 03:29:57 cltien + * Use module count to decide which one to access the dac. + * * */ - + /*****************************************************************************/ #include @@ -251,6 +287,7 @@ struct cm_state { unsigned fragsize; unsigned dmasize; unsigned fragsamples; + unsigned dmasamples; /* OSS stuff */ unsigned mapped:1; unsigned ready:1; @@ -275,6 +312,7 @@ struct cm_state { /* --------------------------------------------------------------------- */ static struct cm_state *devs = NULL; +static struct cm_state *devaudio = NULL; static unsigned long wavetable_mem = 0; /* --------------------------------------------------------------------- */ @@ -330,7 +368,7 @@ static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); outw(count, s->iobase + CODEC_CMI_CH0_FRAME2); outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0); - outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2); +// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) @@ -339,13 +377,16 @@ static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); outw(count, s->iobase + CODEC_CMI_CH1_FRAME2); outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0); - outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2); +// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } extern __inline__ unsigned get_dmadac(struct cm_state *s) { unsigned int curr_addr; + if (!s->dma_dac.dmasize || !(s->enable & CM_CENABLE_PE)) + return 0; + curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1); curr_addr -= virt_to_bus(s->dma_dac.rawbuf); curr_addr = s->dma_dac.dmasize - curr_addr; @@ -357,6 +398,9 @@ extern __inline__ unsigned get_dmaadc(struct cm_state *s) { unsigned int curr_addr; + if (!s->dma_adc.dmasize || !(s->enable & CM_CENABLE_RE)) + return 0; + curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1); curr_addr -= virt_to_bus(s->dma_adc.rawbuf); curr_addr = s->dma_adc.dmasize - curr_addr; @@ -420,7 +464,7 @@ static struct { { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, - { 48000, 48000, 48000, 7 } + { 48000, (44100 + 48000) /2, 48000, 7 } }; static void set_dac_rate(struct cm_state *s, unsigned rate) @@ -484,10 +528,12 @@ extern inline void stop_adc(struct cm_state *s) unsigned long flags; spin_lock_irqsave(&s->lock, flags); + /* disable channel */ + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); s->enable &= ~CM_CENABLE_RE; /* disable interrupt */ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* disable channel and reset */ + /* reset */ outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); udelay(10); outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); @@ -499,10 +545,12 @@ extern inline void stop_dac(struct cm_state *s) unsigned long flags; spin_lock_irqsave(&s->lock, flags); + /* disable channel */ s->enable &= ~CM_CENABLE_PE; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable interrupt */ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* disable channel and reset */ + /* reset */ outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); udelay(10); outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); @@ -515,10 +563,10 @@ static void start_dac(struct cm_state *s) spin_lock_irqsave(&s->lock, flags); if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); s->enable |= CM_CENABLE_PE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } - outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); spin_unlock_irqrestore(&s->lock, flags); } @@ -529,10 +577,10 @@ static void start_adc(struct cm_state *s) spin_lock_irqsave(&s->lock, flags); if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) && s->dma_adc.ready) { + outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); s->enable |= CM_CENABLE_RE; outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } - outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); spin_unlock_irqrestore(&s->lock, flags); } @@ -585,16 +633,17 @@ static int prog_dmabuf(struct cm_state *s, unsigned rec) db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) - printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) - printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); @@ -621,17 +670,21 @@ static int prog_dmabuf(struct cm_state *s, unsigned rec) db->fragsize = 1 << db->fragshift; if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; -#if 1 - /* to make fragsize >= 4096 */ - while (db->fragsize < 4096 && db->numfrag >= 4) - { - db->fragsize *= 2; - db->fragshift++; - db->numfrag /= 2; + /* to make fragsize >= 4096 */ +#if 0 + if(s->modem) + { + while (db->fragsize < 4096 && db->numfrag >= 4) + { + db->fragsize *= 2; + db->fragshift++; + db->numfrag /= 2; + } } -#endif +#endif db->fragsamples = db->fragsize >> sample_shift[fmt]; db->dmasize = db->numfrag << db->fragshift; + db->dmasamples = db->dmasize >> sample_shift[fmt]; memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); spin_lock_irqsave(&s->lock, flags); if (rec) { @@ -663,6 +716,7 @@ extern __inline__ void clear_advance(struct cm_state *s) len -= x; } memset(buf + bptr, c, len); + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); } /* call with spinlock held! */ @@ -750,25 +804,26 @@ static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) unsigned int intsrc, intstat; /* fastpath out, to ease interrupt sharing */ - intsrc = inb(s->iobase + CODEC_CMI_INT_STATUS); - if (!(intsrc & (CM_INT_CH0 | CM_INT_CH1))) + intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); + if (!(intsrc & 0x80000000)) return; spin_lock(&s->lock); intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* disable interrupt */ + /* acknowledge interrupt */ if (intsrc & CM_INT_CH0) + { outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + udelay(10); + outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + } if (intsrc & CM_INT_CH1) + { outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + udelay(10); + outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + } cm_update_ptr(s); -#ifdef SOUND_CONFIG_CMPCI_MIDI cm_handle_midi(s); -#endif - /* enable interrupt */ - if (intsrc & CM_INT_CH0) - outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - if (intsrc & CM_INT_CH1) - outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); spin_unlock(&s->lock); } @@ -786,7 +841,7 @@ static void cm_midi_timer(unsigned long data) /* --------------------------------------------------------------------- */ -static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n"; +static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; #ifdef CONFIG_SOUND_CMPCI /* support multiple chips */ #define VALIDATE_STATE(s) @@ -806,6 +861,7 @@ static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n"; #define MT_5MUTE 2 #define MT_4MUTEMONO 3 #define MT_6MUTE 4 +#define MT_5MUTEMONO 5 static const struct { unsigned left; @@ -816,7 +872,7 @@ static const struct { } mixtable[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 }, [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 }, - [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, CODEC_CMI_MIXER2, MT_4MUTEMONO, 0x01, 0x01 }, + [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 } @@ -849,10 +905,16 @@ static int return_mixval(struct cm_state *s, unsigned i, int *arg) r = l; break; + case MT_5MUTEMONO: + r = l; + rl = 100 - 3 * ((l >> 3) & 31); + rr = rl; + break; + case MT_5MUTE: default: - rl = 100 - 3 * (l & 31); - rr = 100 - 3 * (r & 31); + rl = 100 - 3 * ((l >> 3) & 31); + rr = 100 - 3 * ((r >> 3) & 31); break; case MT_6MUTE: @@ -990,7 +1052,7 @@ static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) } spin_lock_irqsave(&s->lock, flags); wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); - wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | j>>1); + wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1)); spin_unlock_irqrestore(&s->lock, flags); return 0; @@ -1039,6 +1101,14 @@ static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2); break; + case MT_5MUTEMONO: + r = l; + rl = l < 4 ? 0 : (l - 5) / 3; + rr = rl >> 2; + wrmixer(s, mixtable[i].left, rl<<3); + outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2); + break; + case MT_5MUTE: rl = l < 4 ? 0 : (l - 5) / 3; rr = r < 4 ? 0 : (r - 5) / 3; @@ -1122,8 +1192,6 @@ static /*const*/ struct file_operations cm_mixer_fops = { &cm_release_mixdev, NULL, /* fsync */ NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ NULL, /* lock */ }; @@ -1131,7 +1199,7 @@ static /*const*/ struct file_operations cm_mixer_fops = { static int drain_dac(struct cm_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + struct wait_queue wait = { current, NULL} ; unsigned long flags; int count, tmo; @@ -1155,7 +1223,7 @@ static int drain_dac(struct cm_state *s, int nonblock) tmo = (count * HZ) / s->ratedac; tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "cm: dma timed out??\n"); + printk(KERN_DEBUG "cmpci: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); current->state = TASK_RUNNING; @@ -1202,7 +1270,18 @@ static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *pp start_adc(s); if (file->f_flags & O_NONBLOCK) return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_adc.wait); + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaadc(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.dmasamples); + /* program sample counts */ + outw(s->dma_adc.fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; continue; @@ -1262,7 +1341,18 @@ static ssize_t cm_write(struct file *file, const char *buffer, size_t count, lof start_dac(s); if (file->f_flags & O_NONBLOCK) return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac.wait); + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmadac(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.dmasamples); + /* program sample counts */ + outw(s->dma_dac.fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; continue; @@ -1630,9 +1720,11 @@ static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un case SOUND_PCM_READ_BITS: return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg); + case SOUND_PCM_READ_FILTER: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + case SOUND_PCM_WRITE_FILTER: case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: return -EINVAL; } @@ -1721,17 +1813,15 @@ static /*const*/ struct file_operations cm_audio_fops = { &cm_release, NULL, /* fsync */ NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ NULL, /* lock */ }; -#ifdef CONFIG_SOUND_CMPCI_MIDI /* --------------------------------------------------------------------- */ static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct cm_state *s = (struct cm_state *)file->private_data; + struct wait_queue wait = { current , NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1742,7 +1832,10 @@ static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -1753,15 +1846,27 @@ static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) + { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) + { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -1770,13 +1875,17 @@ static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct cm_state *s = (struct cm_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1787,7 +1896,10 @@ static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -1800,15 +1912,25 @@ static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -1821,6 +1943,8 @@ static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count cm_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -1908,14 +2032,15 @@ static int cm_midi_open(struct inode *inode, struct file *file) static int cm_midi_release(struct inode *inode, struct file *file) { struct cm_state *s = (struct cm_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + struct wait_queue wait = { current, NULL}; + unsigned long flags; unsigned count, tmo; VALIDATE_STATE(s); if (file->f_mode & FMODE_WRITE) { - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->midi.owait, &wait); for (;;) { spin_lock_irqsave(&s->lock, flags); @@ -1932,7 +2057,7 @@ static int cm_midi_release(struct inode *inode, struct file *file) } tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "cm: midi timed out??\n"); + printk(KERN_DEBUG "cmpci: midi timed out??\n"); } remove_wait_queue(&s->midi.owait, &wait); current->state = TASK_RUNNING; @@ -1968,15 +2093,11 @@ static /*const*/ struct file_operations cm_midi_fops = { &cm_midi_release, NULL, /* fsync */ NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ NULL, /* lock */ }; -#endif /* --------------------------------------------------------------------- */ -#ifdef CONFIG_SOUND_CMPCI_FM static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static const unsigned char op_offset[18] = { @@ -2144,11 +2265,8 @@ static /*const*/ struct file_operations cm_dmfm_fops = { &cm_dmfm_release, NULL, /* fsync */ NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ NULL, /* lock */ }; -#endif /* CONFIG_SOUND_CMPCI_FM */ /* --------------------------------------------------------------------- */ @@ -2176,15 +2294,39 @@ static struct initvol { }; #ifdef MODULE -__initfunc(int init_module(void)) +static int spdif_loop = 0; +static int four_ch = 0; +static int rear_out = 0; +MODULE_PARM(spdif_loop, "i"); +MODULE_PARM(four_ch, "i"); +MODULE_PARM(rear_out, "i"); + +int __init init_module(void) +#else +#ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP +static int spdif_loop = 1; +#else +static int spdif_loop = 0; +#endif +#ifdef CONFIG_SOUND_CMPCI_4CH +static int four_ch = 1; +#else +static int four_ch = 0; +#endif +#ifdef CONFIG_SOUND_CMPCI_REAR +static int rear_out = 1; #else -__initfunc(int init_cmpci(void)) +static int read_out = 0; +#endif + +int __init init_cmpci(void) #endif { struct cm_state *s; struct pci_dev *pcidev = NULL; mm_segment_t fs; int i, val, index = 0; + struct { unsigned short deviceid; char *devicename; @@ -2200,10 +2342,10 @@ __initfunc(int init_cmpci(void)) if (!pci_present()) /* No PCI bus in this machine! */ #endif return -ENODEV; - printk(KERN_INFO "cm: version v1.1 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "cmpci: version v2.41-nomodem time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) - printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); + printk(KERN_INFO "cmpci: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); #endif while (index < NR_DEVICE && pcidev == NULL && ( (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)) || @@ -2212,7 +2354,7 @@ __initfunc(int init_cmpci(void)) if (pcidev->irq == 0) continue; if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) { - printk(KERN_WARNING "cm: out of memory\n"); + printk(KERN_WARNING "cmpci: out of memory\n"); continue; } /* search device name */ @@ -2233,65 +2375,60 @@ __initfunc(int init_cmpci(void)) s->open_sem = MUTEX; s->magic = CM_MAGIC; s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; -#ifdef CONFIG_SOUND_CMPCI_FM s->iosynth = 0x388; -#endif -#ifdef CONFIG_SOUND_CMPCI_MIDI s->iomidi = 0x330; -#endif if (s->iobase == 0) continue; s->irq = pcidev->irq; if (check_region(s->iobase, CM_EXTENT_CODEC)) { - printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); goto err_region5; } request_region(s->iobase, CM_EXTENT_CODEC, "cmpci"); -#ifdef CONFIG_SOUND_CMPCI_MIDI if (check_region(s->iomidi, CM_EXTENT_MIDI)) { - printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); - goto err_region4; + printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, midi disabled.\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); + s->iomidi = 0; + } + else + { + request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi"); + /* set IO based at 0x330 */ + outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); } - request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi"); - /* set IO based at 0x330 */ - outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); -#endif -#ifdef CONFIG_SOUND_CMPCI_FM if (check_region(s->iosynth, CM_EXTENT_SYNTH)) { - printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); - goto err_region1; + printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, synth disabled.\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); + s->iosynth = 0; + } + else + { + request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM"); + /* enable FM */ + outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL); } - request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM"); - /* enable FM */ - outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL); -#endif /* initialize codec registers */ outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ - outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */ + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ /* reset mixer */ wrmixer(s, DSP_MIX_DATARESETIDX, 0); /* request irq */ if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) { - printk(KERN_ERR "cm: irq %u in use\n", s->irq); + printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); goto err_irq; } - printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n", + printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n", devicename, s->iobase, s->irq); /* register devices */ if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) goto err_dev1; if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) goto err_dev2; -#ifdef CONFIG_SOUND_CMPCI_MIDI - if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) + if (s->iomidi && (s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) goto err_dev3; -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) + if (s->iosynth && (s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) goto err_dev4; -#endif + pci_set_master(pcidev); /* initialize the chips */ fs = get_fs(); set_fs(KERNEL_DS); @@ -2305,6 +2442,38 @@ __initfunc(int init_cmpci(void)) mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); } set_fs(fs); + if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) + { + /* enable SPDIF loop */ + if (spdif_loop) + { + /* turn on spdif-in to spdif-out */ + outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 0x80, s->iobase + CODEC_CMI_FUNCTRL1); + printk(KERN_INFO "cmpci: Enable SPDIF loop\n"); + } + else + outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~0x80, s->iobase + CODEC_CMI_FUNCTRL1); + /* enable 4 channels mode */ + if (four_ch) + { + /* 4 channel mode (analog duplicate) */ + outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) | 0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3); + printk(KERN_INFO "cmpci: Enable 4 channels mode\n"); + /* has separate rear-out jack ? */ + if (rear_out) + { + /* has separate rear out jack */ + outb(inb(s->iobase + CODEC_CMI_MIXER1) & ~0x20, s->iobase + CODEC_CMI_MIXER1); + } + else + { + outb(inb(s->iobase + CODEC_CMI_MIXER1) | 0x20, s->iobase + CODEC_CMI_MIXER1); + printk(KERN_INFO "cmpci: line-in routed as rear-out\n"); + } + } + else + outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) & ~0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3); + } /* queue it for later freeing */ s->next = devs; devs = s; @@ -2318,16 +2487,14 @@ __initfunc(int init_cmpci(void)) err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: - printk(KERN_ERR "cm: cannot register misc device\n"); + printk(KERN_ERR "cmpci: cannot register misc device\n"); free_irq(s->irq, s); err_irq: -#ifdef CONFIG_SOUND_CMPCI_FM - release_region(s->iosynth, CM_EXTENT_SYNTH); + if(s->iosynth) + release_region(s->iosynth, CM_EXTENT_SYNTH); err_region1: -#endif -#ifdef CONFIG_SOUND_CMPCI_MIDI - release_region(s->iomidi, CM_EXTENT_MIDI); -#endif + if(s->iomidi) + release_region(s->iomidi, CM_EXTENT_MIDI); err_region4: release_region(s->iobase, CM_EXTENT_CODEC); err_region5: @@ -2345,11 +2512,6 @@ __initfunc(int init_cmpci(void)) #ifdef MODULE -#if 0 -MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); -#endif - MODULE_AUTHOR("ChenLi Tien, cltien@home.com"); MODULE_DESCRIPTION("CMPCI Audio Driver"); @@ -2361,32 +2523,30 @@ void cleanup_module(void) devs = devs->next; outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ synchronize_irq(); - outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */ + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ free_irq(s->irq, s); /* reset mixer */ wrmixer(s, DSP_MIX_DATARESETIDX, 0); release_region(s->iobase, CM_EXTENT_CODEC); -#ifdef CONFIG_SOUND_CMPCI_MIDI - release_region(s->iomidi, CM_EXTENT_MIDI); -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - release_region(s->iosynth, CM_EXTENT_SYNTH); -#endif + if(s->iomidi) + { + release_region(s->iomidi, CM_EXTENT_MIDI); + unregister_sound_midi(s->dev_midi); + } + if(s->iosynth) + { + release_region(s->iosynth, CM_EXTENT_SYNTH); + unregister_sound_special(s->dev_dmfm); + } unregister_sound_dsp(s->dev_audio); unregister_sound_mixer(s->dev_mixer); -#ifdef CONFIG_SOUND_CMPCI_MIDI - unregister_sound_midi(s->dev_midi); -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - unregister_sound_special(s->dev_dmfm); -#endif kfree_s(s, sizeof(struct cm_state)); } if (wavetable_mem) free_pages(wavetable_mem, 20-PAGE_SHIFT); - printk(KERN_INFO "cm: unloading\n"); + printk(KERN_INFO "cmpci: unloading\n"); } #endif /* MODULE */ diff --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index 80caeedce8c3..2bdba4747b8d 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -1250,7 +1250,7 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long flags; audio_buf_info abinfo; count_info cinfo; - int val, mapped, ret; + int val, mapped, ret, count; int div1, div2; unsigned rate1, rate2; @@ -1330,7 +1330,7 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, stop_dac(s); s->dma_adc.ready = s->dma_dac.ready = 0; /* program channels */ - s->channels = val ? 2 : 1; + s->channels = (val >= 2) ? 2 : 1; prog_codec(s); } return put_user(s->channels, (int *)arg); @@ -1394,7 +1394,10 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; abinfo.fragstotal = s->dma_dac.numfrag; abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; spin_unlock_irqrestore(&s->lock, flags); @@ -1423,9 +1426,11 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EINVAL; spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); - val = s->dma_dac.count; + count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, (int *)arg); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) @@ -1433,7 +1438,10 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, spin_lock_irqsave(&s->lock, flags); solo1_update_ptr(s); cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; cinfo.ptr = s->dma_adc.hwptr; if (s->dma_adc.mapped) s->dma_adc.count &= s->dma_adc.fragsize-1; diff --git a/drivers/sound/maestro.c b/drivers/sound/maestro.c index d513830aab33..cf0246ebc9d6 100644 --- a/drivers/sound/maestro.c +++ b/drivers/sound/maestro.c @@ -208,33 +208,15 @@ /*****************************************************************************/ #include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - - #ifdef MODULE - #include - #ifdef MODVERSIONS - #include - #endif - #endif - #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL} - #define wait_queue_head_t struct wait_queue * - #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK) - #define SILLY_INIT_SEM(SEM) SEM=MUTEX; - #define init_waitqueue_head init_waitqueue - #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC) - #define SILLY_OFFSET(VMA) ((VMA)->vm_offset) - - -#else - - #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start) - #define SILLY_INIT_SEM(SEM) init_MUTEX(&SEM) - #define SILLY_MAKE_INIT(FUNC) __init FUNC - #define SILLY_OFFSET(VMA) ((VMA)->vm_pgoff) - - -#endif +#include + +#define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL} +#define wait_queue_head_t struct wait_queue * +#define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK) +#define SILLY_INIT_SEM(SEM) SEM=MUTEX; +#define init_waitqueue_head init_waitqueue +#define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC) +#define SILLY_OFFSET(VMA) ((VMA)->vm_offset) #include #include diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index c70f7f1da977..3dcea50a905a 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -868,7 +868,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name); int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, int attributes) { + struct ncp_server *server = NCP_SERVER(dir); int error, result; + int opmode; struct ncpfs_inode_info finfo; __u8 _name[dentry->d_name.len + 1]; @@ -886,18 +888,25 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode); io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir)); error = -EACCES; - result = ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name, + result = ncp_open_create_file_or_subdir(server, dir, _name, OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, attributes, AR_READ | AR_WRITE, &finfo.nw_info); - if (!result) { - finfo.nw_info.access = O_RDWR; - error = ncp_instantiate(dir, dentry, &finfo); - } else { - if (result == 0x87) error = -ENAMETOOLONG; - DPRINTK(KERN_DEBUG "ncp_create: %s/%s failed\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - } - + opmode = O_RDWR; + if (result) { + result = ncp_open_create_file_or_subdir(server, dir, _name, + OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, + attributes, AR_WRITE, &finfo.nw_info); + if (result) { + if (result == 0x87) + error = -ENAMETOOLONG; + DPRINTK("ncp_create: %s/%s failed\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + goto out; + } + opmode = O_WRONLY; + } + finfo.nw_info.access = opmode; + error = ncp_instantiate(dir, dentry, &finfo); out: return error; } diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index bb61e2464b9a..0ec1c3896fe5 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -36,9 +36,8 @@ static int ncp_fsync(struct file *file, struct dentry *dentry) */ int ncp_make_open(struct inode *inode, int right) { - int error, result; + int error; int access; - struct nw_file_info finfo; error = -EINVAL; if (!inode) { @@ -53,6 +52,9 @@ int ncp_make_open(struct inode *inode, int right) error = -EACCES; lock_super(inode->i_sb); if (!NCP_FINFO(inode)->opened) { + int result; + struct nw_file_info finfo; + finfo.i.dirEntNum = NCP_FINFO(inode)->dirEntNum; finfo.i.volNumber = NCP_FINFO(inode)->volNumber; /* tries max. rights */ @@ -62,10 +64,20 @@ int ncp_make_open(struct inode *inode, int right) 0, AR_READ | AR_WRITE, &finfo); if (!result) goto update; - finfo.access = O_RDONLY; - result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), + switch (right) { + case O_RDONLY: + finfo.access = O_RDONLY; + result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), + NULL, NULL, OC_MODE_OPEN, + 0, AR_READ, &finfo); + break; + case O_WRONLY: + finfo.access = O_WRONLY; + result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), NULL, NULL, OC_MODE_OPEN, - 0, AR_READ, &finfo); + 0, AR_WRITE, &finfo); + break; + } if (result) { #ifdef NCPFS_PARANOIA printk(KERN_DEBUG "ncp_make_open: failed, result=%d\n", result); @@ -134,7 +146,7 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) error = ncp_make_open(inode, O_RDONLY); if (error) { - printk(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); + DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); goto out; } @@ -212,9 +224,9 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) errno = 0; if (!count) goto out; - errno = ncp_make_open(inode, O_RDWR); + errno = ncp_make_open(inode, O_WRONLY); if (errno) { - printk(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); + DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } pos = *ppos; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 6027c3b84c1c..0d565a44e764 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -691,7 +691,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n", attr->ia_size); - if ((result = ncp_make_open(inode, O_RDWR)) < 0) { + if ((result = ncp_make_open(inode, O_WRONLY)) < 0) { return -EACCES; } ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index a7f767e38acc..971d9995a078 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -307,6 +307,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, default: return -EINVAL; } + /* locking needs both read and write access */ if ((result = ncp_make_open(inode, O_RDWR)) != 0) { return result; diff --git a/fs/proc/array.c b/fs/proc/array.c index 35f092d1392b..47021816fe59 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -248,9 +248,10 @@ static int get_kstat(char * buffer) unsigned long ticks; ticks = jiffies * smp_num_cpus; +#ifndef CONFIG_ARCH_S390 for (i = 0 ; i < NR_IRQS ; i++) sum += kstat_irqs(i); - +#endif #ifdef __SMP__ len = sprintf(buffer, "cpu %u %u %u %lu\n", @@ -274,8 +275,12 @@ static int get_kstat(char * buffer) "disk_rblk %u %u %u %u\n" "disk_wblk %u %u %u %u\n" "page %u %u\n" +#ifdef CONFIG_ARCH_S390 + "swap %u %u\n", +#else "swap %u %u\n" "intr %u", +#endif #else len = sprintf(buffer, "cpu %u %u %u %lu\n" @@ -285,8 +290,12 @@ static int get_kstat(char * buffer) "disk_rblk %u %u %u %u\n" "disk_wblk %u %u %u %u\n" "page %u %u\n" +#ifdef CONFIG_ARCH_S390 + "swap %u %u\n", +#else "swap %u %u\n" "intr %u", +#endif kstat.cpu_user, kstat.cpu_nice, kstat.cpu_system, @@ -305,10 +314,14 @@ static int get_kstat(char * buffer) kstat.pgpgin, kstat.pgpgout, kstat.pswpin, +#ifdef CONFIG_ARCH_S390 + kstat.pswpout); +#else kstat.pswpout, sum); for (i = 0 ; i < NR_IRQS ; i++) len += sprintf(buffer + len, " %u", kstat_irqs(i)); +#endif len += sprintf(buffer + len, "\nctxt %u\n" "btime %lu\n" @@ -850,7 +863,6 @@ static inline char * task_sig(struct task_struct *p, char *buffer) buffer += sprintf(buffer, "SigBlk:\t"); buffer = render_sigset_t(&p->blocked, buffer); *buffer++ = '\n'; - collect_sigign_sigcatch(p, &ign, &catch); buffer += sprintf(buffer, "SigIgn:\t"); buffer = render_sigset_t(&ign, buffer); @@ -877,7 +889,7 @@ static int get_status(int pid, char * buffer) { char * orig = buffer; struct task_struct *tsk; -#ifdef CONFIG_ARCH_S390 +#if __s390__ int line,len; #endif @@ -891,7 +903,7 @@ static int get_status(int pid, char * buffer) buffer = task_mem(tsk, buffer); buffer = task_sig(tsk, buffer); buffer = task_cap(tsk, buffer); -#ifdef CONFIG_ARCH_S390 +#if __s390__ for(line=0;(len=sprintf_regs(line,buffer,tsk,NULL,NULL))!=0;line++) buffer+=len; #endif @@ -1302,7 +1314,9 @@ extern int get_device_list(char *); extern int get_partition_list(char *); extern int get_filesystem_list(char *); extern int get_filesystem_info( char * ); +#ifndef CONFIG_ARCH_S390 extern int get_irq_list(char *); +#endif extern int get_dma_list(char *); extern int get_cpuinfo(char *); extern int get_pci_list(char *); @@ -1366,10 +1380,10 @@ static long get_root_array(char * page, int type, char **start, case PROC_PARTITIONS: return get_partition_list(page); - +#ifndef CONFIG_ARCH_S390 case PROC_INTERRUPTS: return get_irq_list(page); - +#endif case PROC_FILESYSTEMS: return get_filesystem_list(page); #ifndef CONFIG_ARCH_S390 diff --git a/include/asm-s390/atomic.h b/include/asm-s390/atomic.h index 10930e781d06..719a93d20c31 100644 --- a/include/asm-s390/atomic.h +++ b/include/asm-s390/atomic.h @@ -28,7 +28,7 @@ typedef struct { volatile int counter; } atomic_t __attribute__ ((aligned (4))); static __inline__ int atomic_read(atomic_t *v) { int retval; - __asm__ __volatile("bcr 15,0\n\t" + __asm__ __volatile__("bcr 15,0\n\t" "l %0,%1" : "=d" (retval) : "m" (*v) ); return retval; diff --git a/include/asm-s390/irq.h b/include/asm-s390/irq.h index 173f49bab659..9fd707e6fd50 100644 --- a/include/asm-s390/irq.h +++ b/include/asm-s390/irq.h @@ -1,24 +1,735 @@ +#ifndef __irq_h +#define __irq_h + +#include + +/* + * How many IRQ's for S390 ?!? + */ +#define __MAX_SUBCHANNELS 65536 +#define NR_IRQS __MAX_SUBCHANNELS + +#define INVALID_STORAGE_AREA ((void *)(-1 - 0x3FFF )) + +extern int disable_irq(unsigned int); +extern int enable_irq(unsigned int); + +/* + * Interrupt controller descriptor. This is all we need + * to describe about the low-level hardware. + */ +struct hw_interrupt_type { + const char *typename; + int (*handle)(unsigned int irq, + int cpu, + struct pt_regs * regs); + int (*enable) (unsigned int irq); + int (*disable)(unsigned int irq); +}; + +/* + * Status: reason for being disabled: somebody has + * done a "disable_irq()" or we must not re-enter the + * already executing irq.. + */ +#define IRQ_INPROGRESS 1 +#define IRQ_DISABLED 2 +#define IRQ_PENDING 4 + +/* + * path management control word + */ +typedef struct { + unsigned long intparm; /* interruption parameter */ + unsigned int res0 : 2; /* reserved zeros */ + unsigned int isc : 3; /* interruption sublass */ + unsigned int res5 : 3; /* reserved zeros */ + unsigned int ena : 1; /* enabled */ + unsigned int lm : 2; /* limit mode */ + unsigned int mme : 2; /* measurement-mode enable */ + unsigned int mp : 1; /* multipath mode */ + unsigned int tf : 1; /* timing facility */ + unsigned int dnv : 1; /* device number valid */ + unsigned int dev : 16; /* device number */ + unsigned char lpm; /* logical path mask */ + unsigned char pnom; /* path not operational mask */ + unsigned char lpum; /* last path used mask */ + unsigned char pim; /* path installed mask */ + unsigned short mbi; /* measurement-block index */ + unsigned char pom; /* path operational mask */ + unsigned char pam; /* path available mask */ + unsigned char chpid[8]; /* CHPID 0-7 (if available) */ + unsigned int unused1 : 8; /* reserved zeros */ + unsigned int st : 3; /* subchannel type */ + unsigned int unused2 : 20; /* reserved zeros */ + unsigned int csense : 1; /* concurrent sense; can be enabled ...*/ + /* ... per MSCH, however, if facility */ + /* ... is not installed, this results */ + /* ... in an operand exception. */ + } pmcw_t; + +/* + * subchannel status word + */ +typedef struct { + unsigned int key : 4; /* subchannel key */ + unsigned int sctl : 1; /* suspend control */ + unsigned int eswf : 1; /* ESW format */ + unsigned int cc : 2; /* deferred condition code */ + unsigned int fmt : 1; /* format */ + unsigned int pfch : 1; /* prefetch */ + unsigned int isic : 1; /* initial-status interruption control */ + unsigned int alcc : 1; /* address-limit checking control */ + unsigned int ssi : 1; /* supress-suspended interruption */ + unsigned int zcc : 1; /* zero condition code */ + unsigned int ectl : 1; /* extended control */ + unsigned int pno : 1; /* path not operational */ + unsigned int res : 1; /* reserved */ + unsigned int fctl : 3; /* function control */ + unsigned int actl : 7; /* activity control */ + unsigned int stctl : 5; /* status control */ + unsigned long cpa; /* channel program address */ + unsigned int dstat : 8; /* device status */ + unsigned int cstat : 8; /* subchannel status */ + unsigned int count : 16; /* residual count */ + } scsw_t; + +#define SCSW_FCTL_CLEAR_FUNC 0x1 +#define SCSW_FCTL_HALT_FUNC 0x2 +#define SCSW_FCTL_START_FUNC 0x4 + +#define SCSW_ACTL_SUSPENDED 0x1 +#define SCSW_ACTL_DEVACT 0x2 +#define SCSW_ACTL_SCHACT 0x4 +#define SCSW_ACTL_CLEAR_PEND 0x8 +#define SCSW_ACTL_HALT_PEND 0x10 +#define SCSW_ACTL_START_PEND 0x20 +#define SCSW_ACTL_RESUME_PEND 0x40 + +#define SCSW_STCTL_STATUS_PEND 0x1 +#define SCSW_STCTL_SEC_STATUS 0x2 +#define SCSW_STCTL_PRIM_STATUS 0x4 +#define SCSW_STCTL_INTER_STATUS 0x8 +#define SCSW_STCTL_ALERT_STATUS 0x10 + +#define DEV_STAT_ATTENTION 0x80 +#define DEV_STAT_STAT_MOD 0x40 +#define DEV_STAT_CU_END 0x20 +#define DEV_STAT_BUSY 0x10 +#define DEV_STAT_CHN_END 0x08 +#define DEV_STAT_DEV_END 0x04 +#define DEV_STAT_UNIT_CHECK 0x02 +#define DEV_STAT_UNIT_EXCEP 0x01 + +#define SCHN_STAT_PCI 0x80 +#define SCHN_STAT_INCORR_LEN 0x40 +#define SCHN_STAT_PROG_CHECK 0x20 +#define SCHN_STAT_PROT_CHECK 0x10 +#define SCHN_STAT_CHN_DATA_CHK 0x08 +#define SCHN_STAT_CHN_CTRL_CHK 0x04 +#define SCHN_STAT_INTF_CTRL_CHK 0x02 +#define SCHN_STAT_CHAIN_CHECK 0x01 + +/* + * subchannel information block + */ +typedef struct { + pmcw_t pmcw; /* path management control word */ + scsw_t scsw; /* subchannel status word */ + char mda[12]; /* model dependent area */ + } schib_t; + +typedef struct { + char cmd_code;/* command code */ + char flags; /* flags, like IDA adressing, etc. */ + unsigned short count; /* byte count */ + void *cda; /* data address */ + } ccw1_t __attribute__ ((aligned(8))); + +#define CCW_FLAG_DC 0x80 +#define CCW_FLAG_CC 0x40 +#define CCW_FLAG_SLI 0x20 +#define CCW_FLAG_SKIP 0x10 +#define CCW_FLAG_PCI 0x08 +#define CCW_FLAG_IDA 0x04 +#define CCW_FLAG_SUSPEND 0x02 + +#define CCW_CMD_BASIC_SENSE 0x04 +#define CCW_CMD_TIC 0x08 +#define CCW_CMD_SENSE_ID 0xE4 +#define CCW_CMD_NOOP 0x03 +#define CCW_CMD_RDC 0x64 +#define CCW_CMD_READ_IPL 0x02 + +#define SENSE_MAX_COUNT 0x20 + +/* + * architectured values for first sense byte + */ +#define SNS0_CMD_REJECT 0x80 +#define SNS_CMD_REJECT SNS0_CMD_REJECT +#define SNS0_INTERVENTION_REQ 0x40 +#define SNS0_BUS_OUT_CHECK 0x20 +#define SNS0_EQUIPMENT_CHECK 0x10 +#define SNS0_DATA_CHECK 0x08 +#define SNS0_OVERRUN 0x04 + +/* + * operation request block + */ +typedef struct { + unsigned long intparm; /* interruption parameter */ + unsigned int key : 4; /* flags, like key, suspend control, etc. */ + unsigned int spnd : 1; /* suspend control */ + unsigned int res1 : 3; /* reserved */ + unsigned int fmt : 1; /* format control */ + unsigned int pfch : 1; /* prefetch control */ + unsigned int isic : 1; /* initial-status-interruption control */ + unsigned int alcc : 1; /* address-limit-checking control */ + unsigned int ssic : 1; /* suppress-suspended-interr. control */ + unsigned int res2 : 3; /* reserved */ + unsigned int lpm : 8; /* logical path mask */ + unsigned int ils : 1; /* incorrect length */ + unsigned int zero : 7; /* reserved zeros */ + ccw1_t *cpa; /* channel program address */ + } __attribute__ ((packed,aligned(4))) orb_t; + +typedef struct { + unsigned int res0 : 4; /* reserved */ + unsigned int pvrf : 1; /* path-verification-required flag */ + unsigned int cpt : 1; /* channel-path timeout */ + unsigned int fsavf : 1; /* Failing storage address validity flag */ + unsigned int cons : 1; /* concurrent-sense */ + unsigned int res8 : 2; /* reserved */ + unsigned int scnt : 6; /* sense count if cons == 1 */ + unsigned int res16 : 16; /* reserved */ + } erw_t; + +/* + * subchannel logout area + */ +typedef struct { + unsigned int res0 : 1; /* reserved */ + unsigned int esf : 7; /* extended status flags */ + unsigned int lpum : 8; /* last path used mask */ + unsigned int res16 : 1; /* reserved */ + unsigned int fvf : 5; /* field-validity flags */ + unsigned int sacc : 2; /* storage access code */ + unsigned int termc : 2; /* termination code */ + unsigned int devsc : 1; /* device-status check */ + unsigned int serr : 1; /* secondary error */ + unsigned int ioerr : 1; /* i/o-error alert */ + unsigned int seqc : 3; /* sequence code */ + } sublog_t ; + +/* + * Format 0 Extended Status Word (ESW) + */ +typedef struct { + sublog_t sublog; /* subchannel logout */ + erw_t erw; /* extended report word */ + void *faddr; /* failing address */ + unsigned int zeros[2]; /* 2 fullwords of zeros */ + } esw0_t; + +/* + * Format 1 Extended Status Word (ESW) + */ +typedef struct { + unsigned char zero0; /* reserved zeros */ + unsigned char lpum; /* last path used mask */ + unsigned short zero16; /* reserved zeros */ + erw_t erw; /* extended report word */ + unsigned int zeros[3]; /* 2 fullwords of zeros */ + } esw1_t; + +/* + * Format 2 Extended Status Word (ESW) + */ +typedef struct { + unsigned char zero0; /* reserved zeros */ + unsigned char lpum; /* last path used mask */ + unsigned short dcti; /* device-connect-time interval */ + erw_t erw; /* extended report word */ + unsigned int zeros[3]; /* 2 fullwords of zeros */ + } esw2_t; + +/* + * Format 3 Extended Status Word (ESW) + */ +typedef struct { + unsigned char zero0; /* reserved zeros */ + unsigned char lpum; /* last path used mask */ + unsigned short res; /* reserved */ + erw_t erw; /* extended report word */ + unsigned int zeros[3]; /* 2 fullwords of zeros */ + } esw3_t; + +typedef union { + esw0_t esw0; + esw1_t esw1; + esw2_t esw2; + esw3_t esw3; + } esw_t; + +/* + * interruption response block + */ +typedef struct { + scsw_t scsw; /* subchannel status word */ + esw_t esw; /* extended status word */ + char ecw[32]; /* extended control word */ + } irb_t __attribute__ ((aligned(4))); + +/* + * TPI info structure + */ +typedef struct { + unsigned int res : 16; /* reserved 0x00000001 */ + unsigned int irq : 16; /* aka. subchannel number */ + unsigned int intparm; /* interruption parameter */ + } tpi_info_t; + + +/* + * This is the "IRQ descriptor", which contains various information + * about the irq, including what kind of hardware handling it has, + * whether it is disabled etc etc. + * + * Pad this out to 32 bytes for cache and indexing reasons. + */ +typedef struct { + unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */ + struct hw_interrupt_type *handler; /* handle/enable/disable functions */ + struct irqaction *action; /* IRQ action list */ + unsigned int unused[3]; + spinlock_t irq_lock; + } irq_desc_t; + +// +// command information word (CIW) layout +// +typedef struct _ciw { + unsigned int et : 2; // entry type + unsigned int reserved : 2; // reserved + unsigned int ct : 4; // command type + unsigned int cmd : 8; // command + unsigned int count : 16; // count + } ciw_t; + +#define CIW_TYPE_RCD 0x0 // read configuration data +#define CIW_TYPE_SII 0x1 // set interface identifier +#define CIW_TYPE_RNI 0x2 // read node identifier + +// +// sense-id response buffer layout +// +typedef struct { + /* common part */ + unsigned char reserved; /* always 0x'FF' */ + unsigned short cu_type; /* control unit type */ + unsigned char cu_model; /* control unit model */ + unsigned short dev_type; /* device type */ + unsigned char dev_model; /* device model */ + unsigned char unused; /* padding byte */ + /* extended part */ + ciw_t ciw[62]; /* variable # of CIWs */ + } __attribute__ ((packed,aligned(4))) senseid_t; + +/* + * sense data + */ +typedef struct { + unsigned char res[32]; /* reserved */ + unsigned char data[32]; /* sense data */ + } sense_t; + /* - * include/asm-s390/irq.h + * device status area, to be provided by the device driver + * when calling request_irq() as parameter "dev_id", later + * tied to the "action" control block. * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Note : No data area must be added after union ii or the + * effective devstat size calculation will fail ! + */ +typedef struct { + unsigned int devno; /* device number, aka. "cuu" from irb */ + unsigned int intparm; /* interrupt parameter */ + unsigned char cstat; /* channel status - accumulated */ + unsigned char dstat; /* device status - accumulated */ + unsigned char lpum; /* last path used mask from irb */ + unsigned char unused; /* not used - reserved */ + unsigned int flag; /* flag : see below */ + unsigned long cpa; /* CCW address from irb at primary status */ + unsigned int rescnt; /* res. count from irb at primary status */ + unsigned int scnt; /* sense count, if DEVSTAT_FLAG_SENSE_AVAIL */ + union { + irb_t irb; /* interruption response block */ + sense_t sense; /* sense information */ + } ii; /* interrupt information */ + } devstat_t; + +#define DEVSTAT_FLAG_SENSE_AVAIL 0x00000001 +#define DEVSTAT_NOT_OPER 0x00000002 +#define DEVSTAT_START_FUNCTION 0x00000004 +#define DEVSTAT_HALT_FUNCTION 0x00000008 +#define DEVSTAT_STATUS_PENDING 0x00000010 +#define DEVSTAT_DEVICE_OWNED 0x00000080 +#define DEVSTAT_FINAL_STATUS 0x80000000 + +/* + * Flags used as input parameters for do_IO() + */ +#define DOIO_EARLY_NOTIFICATION 0x01 /* allow for I/O completion ... */ + /* ... notification after ... */ + /* ... primary interrupt status */ +#define DOIO_RETURN_CHAN_END DOIO_EARLY_NOTIFICATION +#define DOIO_VALID_LPM 0x02 /* LPM input parameter is valid */ +#define DOIO_WAIT_FOR_INTERRUPT 0x04 /* wait synchronously for interrupt */ +#define DOIO_REPORT_ALL 0x08 /* report all interrupt conditions */ +#define DOIO_ALLOW_SUSPEND 0x10 /* allow for channel prog. suspend */ +#define DOIO_DENY_PREFETCH 0x20 /* don't allow for CCW prefetch */ +#define DOIO_SUPPRESS_INTER 0x40 /* suppress intermediate inter. */ + /* ... for suspended CCWs */ + +/* + * do_IO() * - * Derived from "include/asm-i386/irq.h" - * Copyright (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar + * Start a S/390 channel program. When the interrupt arrives + * handle_IRQ_event() is called, which eventually calls the + * IRQ handler, either immediately, delayed (dev-end missing, + * or sense required) or never (no IRQ handler registered - + * should never occur, as the IRQ (subchannel ID) should be + * disabled if no handler is present. Depending on the action + * taken, do_IO() returns : 0 - Success + * -EIO - Status pending + * see : action->dev_id->cstat + * action->dev_id->dstat + * -EBUSY - Device busy + * -ENODEV - Device not operational */ +int do_IO( int irq, /* IRQ aka. subchannel number */ + ccw1_t *cpa, /* logical channel program address */ + unsigned long initparm, /* interruption parameter */ + unsigned char lpm, /* logical path mask */ + unsigned long flag); /* flags : see above */ + +int start_IO( int irq, /* IRQ aka. subchannel number */ + ccw1_t *cpa, /* logical channel program address */ + unsigned long intparm, /* interruption parameter */ + unsigned char lpm, /* logical path mask */ + unsigned long flag); /* flags : see above */ + +int resume_IO( int irq); /* IRQ aka. subchannel number */ + +int halt_IO( int irq, /* IRQ aka. subchannel number */ + unsigned long intparm, /* dummy intparm */ + unsigned int flag); /* possible DOIO_WAIT_FOR_INTERRUPT */ + + +int process_IRQ( struct pt_regs regs, + unsigned int irq, + unsigned int intparm); + + +int enable_cpu_sync_isc ( int irq ); +int disable_cpu_sync_isc( int irq ); + +typedef struct { + int irq; /* irq, aka. subchannel */ + unsigned int devno; /* device number */ + unsigned int status; /* device status */ + senseid_t sid_data; /* senseID data */ + } dev_info_t; + +int get_dev_info( int irq, dev_info_t *); /* to be eliminated - don't use */ + +int get_dev_info_by_irq ( int irq, dev_info_t *pdi); +int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi); + +int get_irq_by_devno( unsigned int devno ); +unsigned int get_devno_by_irq( int irq ); + +int get_irq_first( void ); +int get_irq_next ( int irq ); -#ifndef _ASM_IRQ_H -#define _ASM_IRQ_H +int read_dev_chars( int irq, void **buffer, int length ); +int read_conf_data( int irq, void **buffer, int *length ); -#define TIMER_IRQ 0x1005 +extern int handle_IRQ_event(unsigned int, int cpu, struct pt_regs *); + +extern int set_cons_dev(int irq); +extern int reset_cons_dev(int irq); +extern int wait_cons_dev(int irq); /* - * How many IRQ's for S390 ?!? + * Some S390 specific IO instructions as inline */ -#define NR_IRQS 1024 -extern int disable_irq(unsigned int); -extern int enable_irq(unsigned int); +extern __inline__ int stsch(int irq, volatile schib_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "STSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int msch(int irq, volatile schib_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "MSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int msch_err(int irq, volatile schib_t *addr) +{ + int ccode; + + __asm__ __volatile__( + " lr 1,%1\n" + " msch 0(%2)\n" + "0: ipm %0\n" + " srl %0,28\n" + "1:\n" + ".section .fixup,\"ax\"\n" + "2: l %0,%3\n" + " bras 1,3f\n" + " .long 1b\n" + "3: l 1,0(1)\n" + " br 1\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 0b,2b\n" + ".previous" + : "=d" (ccode) + : "r" (irq | 0x10000L), "a" (addr), "i" (__LC_PGM_ILC) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int tsch(int irq, volatile irb_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "TSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int tpi( volatile tpi_info_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "TPI 0(%1)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int ssch(int irq, volatile orb_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "SSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int rsch(int irq) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "RSCH\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int csch(int irq) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "CSCH\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int hsch(int irq) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "HSCH\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int iac( void) +{ + int ccode; + + __asm__ __volatile__( + "IAC 1\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : : "cc", "1" ); + return ccode; +} + +typedef struct { + unsigned int vrdcdvno : 16; /* device number (input) */ + unsigned int vrdclen : 16; /* data block length (input) */ + unsigned int vrdcvcla : 8; /* virtual device class (output) */ + unsigned int vrdcvtyp : 8; /* virtual device type (output) */ + unsigned int vrdcvsta : 8; /* virtual device status (output) */ + unsigned int vrdcvfla : 8; /* virtual device flags (output) */ + unsigned int vrdcrccl : 8; /* real device class (output) */ + unsigned int vrdccrty : 8; /* real device type (output) */ + unsigned int vrdccrmd : 8; /* real device model (output) */ + unsigned int vrdccrft : 8; /* real device feature (output) */ + } __attribute__ ((packed,aligned(4))) diag210_t; + +void VM_virtual_device_info( unsigned int devno, /* device number */ + senseid_t *ps ); /* ptr to senseID data */ + +extern __inline__ int diag210( diag210_t * addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + ".long 0x83110210\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "a" (addr) + : "cc", "1" ); + return ccode; +} + +/* + * Various low-level irq details needed by irq.c, process.c, + * time.c, io_apic.c and smp.c + * + * Interrupt entry/exit code at both C and assembly level + */ + +void mask_irq(unsigned int irq); +void unmask_irq(unsigned int irq); + +#define MAX_IRQ_SOURCES 128 + +extern spinlock_t irq_controller_lock; + +#ifdef __SMP__ + +#include + +static inline void irq_enter(int cpu, unsigned int irq) +{ + hardirq_enter(cpu); + while (test_bit(0,&global_irq_lock)) { + eieio(); + } +} + +static inline void irq_exit(int cpu, unsigned int irq) +{ + hardirq_exit(cpu); + release_irqlock(cpu); +} + + +#else + +#define irq_enter(cpu, irq) (++local_irq_count[cpu]) +#define irq_exit(cpu, irq) (--local_irq_count[cpu]) + +#endif + +#define __STR(x) #x +#define STR(x) __STR(x) + +#ifdef __SMP__ + +/* + * SMP has a few special interrupts for IPI messages + */ + +#endif /* __SMP__ */ + +/* + * x86 profiling function, SMP safe. We might want to do this in + * assembly totally? + */ +extern unsigned long _stext; +static inline void s390_do_profile (unsigned long addr) +{ + if (prof_buffer && current->pid) { + addr &= 0x7fffffff; + addr -= (unsigned long) &_stext; + addr >>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (addr > prof_len-1) + addr = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[addr]); + } +} + +#include + +#define s390irq_spin_lock(irq) \ + spin_lock(&(ioinfo[irq]->irq_desc.irq_lock)) + +#define s390irq_spin_unlock(irq) \ + spin_unlock(&(ioinfo[irq]->irq_desc.irq_lock)) + +#define s390irq_spin_lock_irqsave(irq,flags) \ + spin_lock_irqsave(&(ioinfo[irq]->irq_desc.irq_lock), flags) +#define s390irq_spin_unlock_irqrestore(irq,flags) \ + spin_unlock_irqrestore(&(ioinfo[irq]->irq_desc.irq_lock), flags) +#endif -#endif /* _ASM_IRQ_H */ diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index ba2d887baf02..c103b6a5d475 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -2,7 +2,7 @@ * include/asm-s390/lowcore.h * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com), * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) @@ -28,6 +28,7 @@ #define __LC_PGM_ILC 0x08C #define __LC_PGM_INT_CODE 0x08E #define __LC_TRANS_EXC_ADDR 0x090 +#define __LC_SUBCHANNEL_ID 0x0B8 #define __LC_SUBCHANNEL_NR 0x0BA #define __LC_IO_INT_PARM 0x0BC #define __LC_MCCK_CODE 0x0E8 @@ -54,7 +55,7 @@ #define _IO_PSW_MASK 0x04080000 #define _USER_PSW_MASK 0x070DC000/* DAT, IO, EXT, Home-space */ #define _PSW_IO_WAIT 0x020A0000/* IO, Wait */ -#define _WAIT_PSW_MASK 0x070EC000/* DAT, IO, EXT, Wait, Home-space */ +#define _WAIT_PSW_MASK 0x070E0000/* DAT, IO, EXT, Wait, Home-space */ #define _DW_PSW_MASK 0x000A0000/* disabled wait PSW mask */ #define _PRIMARY_MASK 0x0000 /* MASK for SACF */ #define _SECONDARY_MASK 0x0100 /* MASK for SACF */ diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index ac9de9117769..16fd5335db86 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -2,7 +2,7 @@ * include/asm-s390/pgtable.h * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner * * Derived from "include/asm-i386/pgtable.h" @@ -284,7 +284,7 @@ static inline void flush_tlb_range(struct mm_struct * mm, * The Kernel segment-tables includes the User segment-table */ -#define _SEGMENT_TABLE (_USER_SEG_TABLE_LEN) +#define _SEGMENT_TABLE (_USER_SEG_TABLE_LEN|0x80000000) #define _KERNSEG_TABLE (_KERNEL_SEG_TABLE_LEN) /* * No mapping available diff --git a/include/asm-s390/s390-regs-common.h b/include/asm-s390/s390-regs-common.h index 2f48c3978b14..586d92cae86a 100644 --- a/include/asm-s390/s390-regs-common.h +++ b/include/asm-s390/s390-regs-common.h @@ -2,7 +2,7 @@ * include/asm-s390/s390-regs-common.h * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * * this file is designed to keep as much compatibility between @@ -84,8 +84,11 @@ typedef struct /* Sequence of bytes for breakpoint illegal instruction. */ -#define BREAKPOINT {0x0,0x1} -#define BREAKPOINT_U16 ((__u16)0x0001) +#define S390_BREAKPOINT {0x0,0x1} +#define S390_BREAKPOINT_U16 ((__u16)0x0001) +#define S390_SYSCALL_OPCODE ((__u16)0x0a00) +#define S390_SYSCALL_SIZE 2 +#define ADDR_BITS_REMOVE(addr) ((addr)&0x7fffffff) #endif #endif diff --git a/include/asm-s390/s390io.h b/include/asm-s390/s390io.h new file mode 100644 index 000000000000..1b2b4cc73e57 --- /dev/null +++ b/include/asm-s390/s390io.h @@ -0,0 +1,81 @@ +/* + * arch/s390/kernel/s390io.h + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + */ + +#ifndef __s390io_h +#define __s390io_h + +/* + * Private data structure used by do_IO() + * + * Note : If bit flags are added, the "unused" value must be + * decremented accordingly ! + */ +typedef struct _ioinfo { + unsigned int irq; /* aka. subchannel number */ + spinlock_t irq_lock; /* irq lock */ + union { + unsigned int info; + struct { + unsigned int busy : 1; /* device currently in use */ + unsigned int oper : 1; /* device is operational */ + unsigned int fast : 1; /* post with "channel end", ... */ + /* ... don't wait for "device end" */ + /* ... from do_IO() parameters */ + unsigned int ready : 1; /* interrupt handler registered */ + unsigned int haltio : 1; /* halt_IO in process */ + unsigned int doio : 1; /* do_IO in process */ + unsigned int doio_q : 1; /* do_IO queued - only possible ... */ + /* ... if 'fast' is set too */ + unsigned int w4final : 1; /* wait for final status, internally */ + /* ... used with 'fast' setting only */ + unsigned int repall : 1; /* report every interrupt status */ + unsigned int unready : 1; /* deregister irq handler in process */ + unsigned int d_disable : 1; /* delayed disabling required */ + unsigned int w4sense : 1; /* SENSE status pending */ + unsigned int syncio : 1; /* synchronous I/O requested */ + unsigned int consns : 1; /* concurrent sense is available */ + unsigned int delsense : 1; /* delayed SENSE required */ + unsigned int s_pend : 1; /* status pending condition */ + unsigned int unused : 16; /* unused */ + } __attribute__ ((packed)) flags; + } ui; + unsigned long u_intparm; /* user interruption parameter */ + senseid_t senseid; /* SenseID info */ + irq_desc_t irq_desc; /* irq descriptor */ + unsigned int lpm; /* logical path mask to be used ... */ + /* ... from do_IO() parms. Only ... */ + /* ... valid if vlpm is set too. */ + schib_t schib; /* subchannel information block */ + orb_t orb; /* operation request block */ + devstat_t devstat; /* device status */ + ccw1_t *qcpa; /* queued channel program */ + ccw1_t senseccw; /* ccw for sense command */ + unsigned int stctl; /* accumulated status control from irb */ + unsigned int qintparm; /* queued interruption parameter */ + unsigned long qflag; /* queued flags */ + unsigned char qlpm; /* queued logical path mask */ + + struct _ioinfo *prev; + struct _ioinfo *next; + + } __attribute__ ((aligned(8))) ioinfo_t; + +#define IOINFO_FLAGS_BUSY 0x80000000 +#define IOINFO_FLAGS_OPER 0x40000000 +#define IOINFO_FLAGS_FAST 0x20000000 +#define IOINFO_FLAGS_READY 0x10000000 +#define IOINFO_FLAGS_HALTIO 0x08000000 +#define IOINFO_FLAGS_DOIO 0x04000000 +#define IOINFO_FLAGS_DOIO_Q 0x02000000 +#define IOINFO_FLAGS_W4FINAL 0x01000000 +#define IOINFO_FLAGS_REPALL 0x00800000 + +extern ioinfo_t *ioinfo[]; + +#endif /* __s390io_h */ + diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h index 6a9449a5288f..9389e90988b1 100644 --- a/include/asm-s390/setup.h +++ b/include/asm-s390/setup.h @@ -2,7 +2,7 @@ * include/asm-s390/setup.h * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation */ #ifndef _ASM_S390_SETUP_H diff --git a/include/asm-s390/sigcontext.h b/include/asm-s390/sigcontext.h index 0080daad7815..fdf15d2dad0b 100644 --- a/include/asm-s390/sigcontext.h +++ b/include/asm-s390/sigcontext.h @@ -2,14 +2,12 @@ * include/asm-s390/sigcontext.h * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation */ #ifndef _ASM_S390_SIGCONTEXT_H #define _ASM_S390_SIGCONTEXT_H - -#include - +#include /* Has to be at least _NSIG_WORDS from asm/signal.h @@ -20,11 +18,19 @@ #define __SIGNAL_FRAMESIZE STACK_FRAME_OVERHEAD #define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) #define SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) -struct sigcontext_struct { - int signal; - unsigned long handler; - user_regs_struct *regs; + +typedef struct +{ + s390_regs_common regs; + s390_fp_regs fpregs; +} sigregs; + +struct sigcontext +{ unsigned long oldmask[_SIGCONTEXT_NSIG_WORDS]; + sigregs *sregs; }; + #endif + diff --git a/include/asm-s390/uaccess.h b/include/asm-s390/uaccess.h index ca4112d3f022..ad3eac01734d 100644 --- a/include/asm-s390/uaccess.h +++ b/include/asm-s390/uaccess.h @@ -2,7 +2,7 @@ * include/asm-s390/uaccess.h * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com) * @@ -321,6 +321,20 @@ extern int __put_user_bad(void); __gu_err; \ }) +/* + * The "xxx_ret" versions return constant specified in third argument, if + * something bad happens. These macros can be optimized for the + * case of just returning from the function xxx_ret is used. + */ + +#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; }) + +#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; }) + +#define __put_user_ret(x,ptr,ret) ({ if (__put_user(x,ptr)) return ret; }) + +#define __get_user_ret(x,ptr,ret) ({ if (__get_user(x,ptr)) return ret; }) + extern int __get_user_bad(void); /* @@ -427,30 +441,28 @@ strncpy_from_user(char *dst, const char *src, long count) " sacf 512\n" "0: ic 3,0(%0,4)\n" "1: stc 3,0(%0,2)\n" - " ahi %0,1\n" - " cr %0,%3\n" - " je 2f\n" " ltr 3,3\n" - " jne 0b\n" + " jz 2f\n" + " ahi %0,1\n" + " clr %0,%3\n" + " jl 0b\n" "2: sacf 0(1)\n" - "3:\n" ".section .fixup,\"ax\"\n" - "4: sacf 0(1)\n" - " lhi %0,%h4\n" - " bras 3,5f\n" - " .long 3b\n" - "5: l 3,0(3)\n" + "3: lhi %0,%h4\n" + " basr 3,0\n" + " l 3,4f-.(3)\n" " br 3\n" + "4: .long 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" " .align 4\n" - " .long 0b,4b\n" - " .long 1b,4b\n" + " .long 0b,3b\n" + " .long 1b,3b\n" ".previous" : "=&a" (len) : "a" (dst), "d" (src), "d" (count), "K" (-EFAULT) - : "1", "2" ,"3", "4" ); + : "1", "2", "3", "4", "memory" ); return len; } diff --git a/include/asm-s390/ucontext.h b/include/asm-s390/ucontext.h index 1536f4236c39..b4323830af9f 100644 --- a/include/asm-s390/ucontext.h +++ b/include/asm-s390/ucontext.h @@ -16,4 +16,6 @@ struct ucontext { sigset_t uc_sigmask; /* mask last for extensibility */ }; + + #endif /* !_ASM_S390_UCONTEXT_H */ diff --git a/include/linux/apm_bios.h b/include/linux/apm_bios.h index caf0b4b76430..86e8edeca46c 100644 --- a/include/linux/apm_bios.h +++ b/include/linux/apm_bios.h @@ -3,7 +3,7 @@ /* * Include file for the interface to an APM BIOS - * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com) + * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.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 @@ -46,46 +46,61 @@ struct apm_bios_info { #define APM_BIOS_DISENGAGED 0x0010 /* - * Maximum number of events stored + * The APM function codes */ -#define APM_MAX_EVENTS 20 +#define APM_FUNC_INST_CHECK 0x5300 +#define APM_FUNC_REAL_CONN 0x5301 +#define APM_FUNC_16BIT_CONN 0x5302 +#define APM_FUNC_32BIT_CONN 0x5303 +#define APM_FUNC_DISCONN 0x5304 +#define APM_FUNC_IDLE 0x5305 +#define APM_FUNC_BUSY 0x5306 +#define APM_FUNC_SET_STATE 0x5307 +#define APM_FUNC_ENABLE_PM 0x5308 +#define APM_FUNC_RESTORE_BIOS 0x5309 +#define APM_FUNC_GET_STATUS 0x530a +#define APM_FUNC_GET_EVENT 0x530b +#define APM_FUNC_GET_STATE 0x530c +#define APM_FUNC_ENABLE_DEV_PM 0x530d +#define APM_FUNC_VERSION 0x530e +#define APM_FUNC_ENGAGE_PM 0x530f +#define APM_FUNC_GET_CAP 0x5310 +#define APM_FUNC_RESUME_TIMER 0x5311 +#define APM_FUNC_RESUME_ON_RING 0x5312 +#define APM_FUNC_TIMER 0x5313 /* - * The per-file APM data + * Function code for APM_FUNC_RESUME_TIMER */ -struct apm_bios_struct { - int magic; - struct apm_bios_struct * next; - int suser; - int suspends_pending; - int standbys_pending; - int suspends_read; - int standbys_read; - int event_head; - int event_tail; - apm_event_t events[APM_MAX_EVENTS]; -}; +#define APM_FUNC_DISABLE_TIMER 0 +#define APM_FUNC_GET_TIMER 1 +#define APM_FUNC_SET_TIMER 2 + +/* + * Function code for APM_FUNC_RESUME_ON_RING + */ +#define APM_FUNC_DISABLE_RING 0 +#define APM_FUNC_ENABLE_RING 1 +#define APM_FUNC_GET_RING 2 /* - * The magic number in apm_bios_struct + * Function code for APM_FUNC_TIMER_STATUS */ -#define APM_BIOS_MAGIC 0x4101 +#define APM_FUNC_TIMER_DISABLE 0 +#define APM_FUNC_TIMER_ENABLE 1 +#define APM_FUNC_TIMER_GET 2 /* * in init/main.c */ extern struct apm_bios_info apm_bios_info; -extern void apm_bios_init(void); +extern void apm_init(void); extern void apm_setup(char *, int *); extern int apm_register_callback(int (*callback)(apm_event_t)); extern void apm_unregister_callback(int (*callback)(apm_event_t)); -extern void apm_power_off(void); -extern int apm_display_blank(void); -extern int apm_display_unblank(void); - #endif /* __KERNEL__ */ /* @@ -97,6 +112,14 @@ extern int apm_display_unblank(void); #define APM_STATE_OFF 0x0003 #define APM_STATE_BUSY 0x0004 #define APM_STATE_REJECT 0x0005 +#define APM_STATE_OEM_SYS 0x0020 +#define APM_STATE_OEM_DEV 0x0040 + +#define APM_STATE_DISABLE 0x0000 +#define APM_STATE_ENABLE 0x0001 + +#define APM_STATE_DISENGAGE 0x0000 +#define APM_STATE_ENGAGE 0x0001 /* * Events (results of Get PM Event) @@ -112,7 +135,7 @@ extern int apm_display_unblank(void); #define APM_USER_STANDBY 0x0009 #define APM_USER_SUSPEND 0x000a #define APM_STANDBY_RESUME 0x000b -#define APM_CAPABILITY_CHANGE 0x000c +#define APM_CAPABILITY_CHANGE 0x000c /* * Error codes @@ -128,14 +151,58 @@ extern int apm_display_unblank(void); #define APM_BAD_DEVICE 0x09 #define APM_BAD_PARAM 0x0a #define APM_NOT_ENGAGED 0x0b -#define APM_BAD_FUNCTION 0x0c +#define APM_BAD_FUNCTION 0x0c #define APM_RESUME_DISABLED 0x0d #define APM_NO_ERROR 0x53 #define APM_BAD_STATE 0x60 #define APM_NO_EVENTS 0x80 #define APM_NOT_PRESENT 0x86 -/* ioctl operations */ +/* + * APM Device IDs + */ +#define APM_DEVICE_BIOS 0x0000 +#define APM_DEVICE_ALL 0x0001 +#define APM_DEVICE_DISPLAY 0x0100 +#define APM_DEVICE_STORAGE 0x0200 +#define APM_DEVICE_PARALLEL 0x0300 +#define APM_DEVICE_SERIAL 0x0400 +#define APM_DEVICE_NETWORK 0x0500 +#define APM_DEVICE_PCMCIA 0x0600 +#define APM_DEVICE_BATTERY 0x8000 +#define APM_DEVICE_OEM 0xe000 +#define APM_DEVICE_OLD_ALL 0xffff +#define APM_DEVICE_CLASS 0x00ff +#define APM_DEVICE_MASK 0xff00 + +#ifdef __KERNEL__ +/* + * This is the "All Devices" ID communicated to the BIOS + */ +#define APM_DEVICE_BALL ((apm_bios_info.version > 0x0100) ? \ + APM_DEVICE_ALL : APM_DEVICE_OLD_ALL) +#endif + +/* + * Battery status + */ +#define APM_MAX_BATTERIES 2 + +/* + * APM defined capability bit flags + */ +#define APM_CAP_GLOBAL_STANDBY 0x0001 +#define APM_CAP_GLOBAL_SUSPEND 0x0002 +#define APM_CAP_RESUME_STANDBY_TIMER 0x0004 /* Timer resume from standby */ +#define APM_CAP_RESUME_SUSPEND_TIMER 0x0008 /* Timer resume from suspend */ +#define APM_CAP_RESUME_STANDBY_RING 0x0010 /* Resume on Ring fr standby */ +#define APM_CAP_RESUME_SUSPEND_RING 0x0020 /* Resume on Ring fr suspend */ +#define APM_CAP_RESUME_STANDBY_PCMCIA 0x0040 /* Resume on PCMCIA Ring */ +#define APM_CAP_RESUME_SUSPEND_PCMCIA 0x0080 /* Resume on PCMCIA Ring */ + +/* + * ioctl operations + */ #include #define APM_IOC_STANDBY _IO('A', 1) diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index cd584c1af250..ff186cadfd80 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -25,7 +25,9 @@ struct kernel_stat { unsigned int dk_drive_wblk[DK_NDRIVE]; unsigned int pgpgin, pgpgout; unsigned int pswpin, pswpout; +#ifndef CONFIG_ARCH_S390 unsigned int irqs[NR_CPUS][NR_IRQS]; +#endif unsigned int ipackets, opackets; unsigned int ierrors, oerrors; unsigned int collisions; @@ -34,6 +36,7 @@ struct kernel_stat { extern struct kernel_stat kstat; +#ifndef CONFIG_ARCH_S390 /* * Number of interrupts per specific IRQ source, since bootup */ @@ -46,5 +49,6 @@ extern inline int kstat_irqs (int irq) return sum; } +#endif #endif /* _LINUX_KERNEL_STAT_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index f218823304b4..8f8c99b06074 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -286,6 +286,7 @@ #define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 #define PCI_DEVICE_ID_COMPAQ_1280 0x3033 #define PCI_DEVICE_ID_COMPAQ_TRIFLEX 0x4000 +#define PCI_DEVICE_ID_COMPAQ_6010 0x6010 #define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 #define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 #define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 @@ -856,6 +857,10 @@ #define PCI_DEVICE_ID_RENDITION_VERITE 0x0001 #define PCI_DEVICE_ID_RENDITION_VERITE2100 0x2000 +#define PCI_VENDOR_ID_RCC 0x1166 +#define PCI_DEVICE_ID_RCC_HE 0x0008 +#define PCI_DEVICE_ID_RCC_LE 0x0009 + #define PCI_VENDOR_ID_TOSHIBA 0x1179 #define PCI_DEVICE_ID_TOSHIBA_601 0x0601 #define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a diff --git a/init/main.c b/init/main.c index dc546ec84a9c..5e584a7f16b4 100644 --- a/init/main.c +++ b/init/main.c @@ -75,8 +75,7 @@ extern int kupdate(void *); extern int kswapd(void *); extern int kpiod(void *); extern void kswapd_setup(void); - -extern void init_IRQ(void); +extern unsigned long init_IRQ( unsigned long); extern void init_modules(void); extern long console_init(long, long); extern void sock_init(void); @@ -157,6 +156,9 @@ extern void ctc_setup(char *str, int *ints); #ifdef CONFIG_IUCV extern void iucv_setup(char *str, int *ints); #endif +#ifdef CONFIG_IUCV +extern void iucv_setup(char *str, int *ints); +#endif #ifdef CONFIG_ARCNET_COM90xxIO extern void com90io_setup(char *str, int *ints); #endif @@ -191,6 +193,9 @@ extern void pcd_setup(char *str, int *ints); #ifdef CONFIG_3215 extern void con3215_setup(char *str, int *ints); #endif +#ifdef CONFIG_3215 +extern void con3215_setup(char *str, int *ints); +#endif #ifdef CONFIG_MDISK extern void mdisk_setup(char *str, int *ints); #endif @@ -578,24 +583,24 @@ static struct dev_name_struct { { "ddv", DDV_MAJOR << 8}, #endif #ifdef CONFIG_MDISK - { "mnd0", (MDISK_MAJOR << MINORBITS)}, - { "mnd1", (MDISK_MAJOR << MINORBITS) + 1}, - { "mnd2", (MDISK_MAJOR << MINORBITS) + 2}, - { "mnd3", (MDISK_MAJOR << MINORBITS) + 3}, - { "mnd4", (MDISK_MAJOR << MINORBITS) + 4}, - { "mnd5", (MDISK_MAJOR << MINORBITS) + 5}, - { "mnd6", (MDISK_MAJOR << MINORBITS) + 6}, - { "mnd7", (MDISK_MAJOR << MINORBITS) + 7}, + { "mnda", (MDISK_MAJOR << MINORBITS)}, + { "mndb", (MDISK_MAJOR << MINORBITS) + 1}, + { "mndc", (MDISK_MAJOR << MINORBITS) + 2}, + { "mndd", (MDISK_MAJOR << MINORBITS) + 3}, + { "mnde", (MDISK_MAJOR << MINORBITS) + 4}, + { "mndf", (MDISK_MAJOR << MINORBITS) + 5}, + { "mndg", (MDISK_MAJOR << MINORBITS) + 6}, + { "mndh", (MDISK_MAJOR << MINORBITS) + 7}, #endif #ifdef CONFIG_DASD - { "dasd0", (DASD_MAJOR << MINORBITS) }, - { "dasd1", (DASD_MAJOR << MINORBITS) + (1 << 2) }, - { "dasd2", (DASD_MAJOR << MINORBITS) + (2 << 2) }, - { "dasd3", (DASD_MAJOR << MINORBITS) + (3 << 2) }, - { "dasd4", (DASD_MAJOR << MINORBITS) + (4 << 2) }, - { "dasd5", (DASD_MAJOR << MINORBITS) + (5 << 2) }, - { "dasd6", (DASD_MAJOR << MINORBITS) + (6 << 2) }, - { "dasd7", (DASD_MAJOR << MINORBITS) + (7 << 2) }, + { "dasda", (DASD_MAJOR << MINORBITS) }, + { "dasdb", (DASD_MAJOR << MINORBITS) + (1 << 2) }, + { "dasdc", (DASD_MAJOR << MINORBITS) + (2 << 2) }, + { "dasdd", (DASD_MAJOR << MINORBITS) + (3 << 2) }, + { "dasde", (DASD_MAJOR << MINORBITS) + (4 << 2) }, + { "dasdf", (DASD_MAJOR << MINORBITS) + (5 << 2) }, + { "dasdg", (DASD_MAJOR << MINORBITS) + (6 << 2) }, + { "dasdh", (DASD_MAJOR << MINORBITS) + (7 << 2) }, #endif { NULL, 0 } }; @@ -651,6 +656,10 @@ static struct kernel_param cooked_params[] __initdata = { { "noapic", ioapic_setup }, { "pirq=", ioapic_pirq_setup }, #endif +#ifdef CONFIG_IUCV + { "iucv=", iucv_setup } , +#endif + #endif #ifdef CONFIG_BLK_DEV_RAM { "ramdisk_start=", ramdisk_start_setup }, @@ -1018,6 +1027,9 @@ static struct kernel_param raw_params[] __initdata = { #ifdef CONFIG_3215 { "condev=", con3215_setup }, #endif +#ifdef CONFIG_3215 + { "condev=", con3215_setup }, +#endif #ifdef CONFIG_MDISK { "mdisk=", mdisk_setup }, #endif @@ -1264,7 +1276,7 @@ asmlinkage void __init start_kernel(void) setup_arch(&command_line, &memory_start, &memory_end); memory_start = paging_init(memory_start,memory_end); trap_init(); - init_IRQ(); + memory_start = init_IRQ( memory_start ); sched_init(); time_init(); parse_options(command_line); diff --git a/ipc/shm.c b/ipc/shm.c index 2b6cd91a3262..04499fe31c0e 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -79,6 +79,7 @@ static int newseg (key_t key, int shmflg, int size) struct shmid_kernel *shp; int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; int id, i; + pte_t tmp_pte; if (size < SHMMIN) return -EINVAL; @@ -107,7 +108,11 @@ found: return -ENOMEM; } - for (i = 0; i < numpages; shp->shm_pages[i++] = 0); + pte_clear(&tmp_pte); + + for (i = 0; i < numpages; i++) + shp->shm_pages[i] = pte_val(tmp_pte); + shm_tot += numpages; shp->u.shm_perm.key = key; shp->u.shm_perm.mode = (shmflg & S_IRWXUGO); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 820b17309abf..6509c0b0ba2d 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -424,3 +424,4 @@ EXPORT_SYMBOL(strnicmp); /* init task, for moving kthread roots - ought to export a function ?? */ EXPORT_SYMBOL(init_task_union); + diff --git a/kernel/panic.c b/kernel/panic.c index 3a8d65ab7d33..0987f9759638 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -16,10 +16,6 @@ #include #include -#ifdef __alpha__ -#include -#endif - asmlinkage void sys_sync(void); /* it's really int */ extern void unblank_console(void); extern int C_A_D; @@ -38,6 +34,9 @@ NORET_TYPE void panic(const char * fmt, ...) { static char buf[1024]; va_list args; +#ifdef CONFIG_ARCH_S390 + unsigned long caller = (unsigned long) __builtin_return_address(0); +#endif va_start(args, fmt); vsprintf(buf, fmt, args); @@ -81,12 +80,8 @@ NORET_TYPE void panic(const char * fmt, ...) printk("Press L1-A to return to the boot prom\n"); } #endif -#ifdef __alpha__ - if (alpha_using_srm) - halt(); -#endif #ifdef CONFIG_ARCH_S390 - disabled_wait(0x1234); + disabled_wait(caller); #endif sti(); for(;;) { diff --git a/net/ipv6/Config.in b/net/ipv6/Config.in index 3372817c3eaa..a2908aec94c5 100644 --- a/net/ipv6/Config.in +++ b/net/ipv6/Config.in @@ -6,8 +6,10 @@ if [ "$CONFIG_IPV6_EUI64" = "y" ]; then bool 'IPv6: disable provider based addresses' CONFIG_IPV6_NO_PB fi if [ "$CONFIG_NETLINK" = "y" ]; then - if [ "$CONFIG_RTNETLINK" = "n" ]; then - bool 'IPv6: routing messages via old netlink' CONFIG_IPV6_NETLINK + if [ "$CONFIG_NETLINK_DEV" != "n" ]; then + if [ "$CONFIG_RTNETLINK" = "n" ]; then + bool 'IPv6: routing messages via old netlink' CONFIG_IPV6_NETLINK + fi fi fi #bool 'IPv6: flow policy support' CONFIG_RT6_POLICY diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index f734a213f1db..df6cd6402d72 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c @@ -74,6 +74,11 @@ static void x25_stop_t20timer(struct x25_neigh *neigh) del_timer(&neigh->t20timer); } +static int x25_t20timer_pending(struct x25_neigh *neigh) +{ + return timer_pending(&neigh->t20timer); +} + /* * This handles all restart and diagnostic frames. */ @@ -84,7 +89,7 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned sho switch (frametype) { case X25_RESTART_REQUEST: x25_stop_t20timer(neigh); - if (neigh->state!=X25_LINK_STATE_2) + if (!x25_t20timer_pending(neigh)) x25_transmit_restart_confirmation(neigh); neigh->state = X25_LINK_STATE_3; break;