ackerman@uvm-gen.UUCP (Steve Ackerman) (01/16/89)
Hello! I'm trying to get Minix 1.3 running on an IBM PS/2 Model 80 with not too much luck. With Dr. Tanenbaum, Stephen Ligett, and Gary Craig's help, I've been trying different methods of attack. Right now, I have an AT&T 6300 with Minix 1.3 connected via serial to the Model 80. I installed Gary Craig's floppy.c diffs and I'm working from there. First, some semi-hard results (semi-hard because I don't want to put my foot in my mouth! :-) ): 1) It appears that there will be a need for an MCA type variable since the MODEL 80 crashes sooner with ps = 1 than it does with ps = 0 2) I can't see any noticeable difference between the old floppy.c and the new floppy.c (as yet) - meaning I think that there is another problem besides the ones the new floppy.c fixes. 3) There is no difference (as yet) with pc_at = 1, and pc_at = 0. 4) By putting printk's in the kernel/proc.c interrupt() function, I've found that a MASSIVE amount of interrupts are being received for the FLOPPY task. This is after the floppy task starts the motor, issues a recalibrate command to Floppy disk controller, and then does a receive(HARDWARE). A printk placed right after the receive in the Floppy task never gets executed. Leading me to believe that the system appears to hang because all its doing is servicing disk interrupts. Also, once the the first disk interrupt is received, the message is sent to FS. Thereafter, all the other messages fail on the call to mini_send since the FS is marked as not waiting. 5) By placing this statement in interrupt(): /* TEST: mask any further disk interrupts * Note: Gary Craig's code will undo this after receive(HARDWARE) * in floppy task */ if (task == FLOPPY) port_out(INT_CTLMASK, 0x40); I was able to continue execution until the system decided it had an Invalid Root File System (even though it didn't). The message "Type space to reboot" appeared. I did, and it received the SPACE key interrupt and rebooted (in)correctly. I think that the reason Minix came up with a bad file system error was probably because the other receive(HARDWARE)s were receiving the initial interrupt cause by the recalibrate command. 6) With masking the disk interrupt off, I can then hit F1/F2 and get a dump (meaning at least the KEYBOARD interrupt works). However, I'm not sure the clock works. If my memory serves, at first I forgot to do a test to see if the interrupt was coming from the clock before I did a: printk("Hello from interrupt! task = %d\n", task); in the interrupt routine. I noticed this a while later - certainly after I had tried the boot image on the Model 80 once or twice, and I don't remember being bombarded with lots of: "Hello from interrupt! task = -3" meaning a clock interrupt was received. This brings me to my question. Recently, Scott Wiesner posted this message: >PS/2's with microchannel architectures use level triggered rather than >edge triggered interrupts. The pic(s) much be programmed at initialization >time for level triggered interrupts. > >The normal interrupt acknowledgment for a PC is not sufficient to lower >the interrupt for a device. All the devices on a PS/2 have some operation >that causes an interrupt to be cleared.. Most devices have their interrupt >line lowered as a result of an operation that would normally be done at >interrupt time anyway such as reading from the device. For the floppy >drives, you must issue a sense interrupt status command to the controller. >For the clock, you've got to set the high bit at io address 0x61. (Don't >remember what this is. Some kind of misc. control register I think. I >believe this is a read/modify/write operation.) I think these were the >only two that required anything special. Does anyone (maybe Scott??) know what the value of the "sense interrupt status" command is? I would greatly appreciate it if some one could post that information so I can attempt sending a "sense interrupt status" command to the FDC in interrupt(). Dr. Tanenbaum and I have concluded that the 8259A controller should be set appropriately for level triggered interrupts by BIOS. Since the keyboard interrupt is being detected correctly, I think the problem lies mainly with the Floppy Disk Controller not "shutting up" after sending one interrupt. If anyone has some more suggestions about what may be wrong, please post! I need all the help I can get. Thanks. Steve
ast@cs.vu.nl (Andy Tanenbaum) (01/17/89)
In article <1023@uvm-gen.UUCP> ackerman@uvm-gen.UUCP (Steve Ackerman) writes:
[discussion of the problem with the PS/2 Model 80].
I completely agree with Steve's analysis. Since the operating system is read in
by bootblok.s, which makes BIOS calls, the hardware is presumably set up
properly, including the 8259A. For a PC, the 8259A is in edge-triggered mode.
After an interrupt, the device is re-enabled on line 1888 of the book. You
get exactly one interrupt this way.
There must be an analogous statement to line 1888 for the microchannel.
My hope is that by adding a line
if (mca) port_out(somewhere, something)
around there, we can get exactly one interrupt, no more and no less.
If anyone who knows the PS/2, or for that matter, the 8259A, has any idea,
please post it.
Andy Tanenbaum (ast@cs.vu.nl)
tom@rsp.UUCP (Thomas Ruf) (01/17/89)
In article <1023@uvm-gen.UUCP>, ackerman@uvm-gen.UUCP (Steve Ackerman) writes: > Does anyone (maybe Scott??) know what the value of the "sense interrupt > status" command is? From "IBM PS/2 Model 50 & 60 Technical Reference, page 4-143" : Sense Interrupt Status Command Format Command Phase 7 6 5 4 3 2 1 0 Byte 0 0 0 0 0 1 0 0 0 Result Phase Byte 0 Status Register 0 (ST 0) Byte 1 Present Cylinder Number (PCN) On Microchannel systems, every device driver definitely MUST DEACTIVATE the corresponding IRQ line before it enables interrupts again. Otherwise the interrupt routine will be reentered forever ... However, I don't know whether the "Sense Interrupt Status Command" will do this for the floppy controller. But I also didn't find any other reference to "interrupt" in the floppy controller section of the above mentioned manual. I hope this helps and good luck Thomas ------------------------------------------------------------------- Thomas Ruf {uunet,mcvax}!unido!rsp!tom RSP Datensysteme GmbH West-Germany Scheffelstrasse 26 D-7590 Achern
crew@pitt.UUCP (Al Crew) (01/18/89)
On the PS/2 problems, quoting the PS/2 Technical Reference: "Service routines must not attempt to end the interrupt sequence (EOI) until it has reset the interrupt line of the device being serviced." I didn't actually look up the bit pattern, but I assume the "ENABLE" in line 1888 of the book is the 8259A's EOI command. This could be a problem in the Minix environment where the interrupt would not be cleared until the floppy task is dispatched. Also, interestingly, the PS/2 will not allow you to set the PIC up in edge-sensitive mode (even accidentally): "The interrupt control circuitry of the system board prevents setting the controller to the edge-sensitive mode by blocking positive edge-sensitive commands to the interrupt controllers." (Yes, I did look up the bit pattern "ENABLE" is EOI.)
evans@ditsyda.oz (Bruce Evans) (01/19/89)
In article <1907@ast.cs.vu.nl> ast@cs.vu.nl (Andy Tanenbaum) writes: >I suspect what they are trying to say is that if the EOI is issued when the >level-triggered interrupt is still asserted, you get another interrupt. That must be it! The EOI is issued in interrupt(), which is the *first* stage of interrupt handling. >[...] The floppy disk >controller must be given some sort of command to tell it to negate the >interrupt. Furthermore, every other device (clock, hard disk, network, >etc) that can generate interrupts has the same problem. The floppy task must not be getting a chance to do this, because the processor is interrupted again as soon as it finishes the low-level interrupt handling. This does not happen for edge-triggered interrupts since the device interrupt request remains asserted. So, the solution may be simply to remove all the EOI's in proc.c and put them in floppy.c. Similarly for *wini.c. The other drivers are probably OK. All except clock.c are low level and already do their own EOI's, and I think the clock interrupt turns off automatically. The EOI's never belonged in proc.c anyway, since they are machine-dependent (pc, at, ps, ...) while the rest is not. I put them elsewhere ages ago, centralized in save(). Even worse for level triggered interrupts :-{. -- Bruce Evans evans@ditsyda.oz.au D
ast@cs.vu.nl (Andy Tanenbaum) (01/21/89)
In article <1259@ditsyda.oz> evans@ditsyda.oz (Bruce Evans) writes: >So, the solution may be simply to remove all the EOI's in proc.c and put >them in floppy.c. Similarly for *wini.c. The other drivers are probably >OK. All except clock.c are low level and already do their own EOI's, and >I think the clock interrupt turns off automatically. First, a number of people seem to agree with the theoretical analysis that by sending a sense command to the floppy disk controller, it will negate its interrupt. Could one of the people with a PS/2 give it a try and see if we are on the right track. Second, I think is is cleaner to have the EOI sent in a single place for all device classes, rather than having every routine have to worry about it on its own. Some day some naive person will write a driver for a new device and not realize that he has to deal with this. I am perfectly happy to move it anywhere else if someone can suggest a suitable place. If it can't be done, it can't be done, but it would be cleaner to do that sort of nasty stuff just once, not all over the place. John Nall made a comment a couple of days ago about world-wide debugging. With Bruce's comments on the PS/2, we now have three continents working on this problem. When I was a kid, I always thought it was a pity that I hadn't been born 50 years earlier, so I could have been a pioneer in, say, amateur radio in the beginning. Nevertheless, trying to solve an obscure software problem with the help of a score of people on three continents (anybody in Japan have any suggestions?) is probably in the same spirit. I think most people on the net take for granted something that was quite inconceivably only 20 years ago. For instance, I never recall having read any science fiction stories called "The Net." Three cheers for USENET. Andy Tanenbaum (ast@cs.vu.nl)
evans@ditsyda.oz (Bruce Evans) (01/25/89)
In article <1941@ast.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) writes: > Second, I think is is cleaner to have the EOI sent in a single place for > all device classes, rather than having every routine have to worry about it I don't object to a single EOI statement done in interrupt(). But what is done now defeats the point of this: it tests for the special cases ps && task == FLOPPY pc_at && task == WINCHESTER to decide the appropiate place to send the EOI to. Since only CLOCK, FLOPPY, PRINTER and WINCHESTER currently depend on the EOI done here, only two cases are being handled generally. All the PS cases for models >= 50 will be different again. Interrupt() must be kept short since it runs with interrupts disabled. So I don't want to have any port_out()s in it (a dozen or so instructions in C compared with 2 in assembler) or lots of cases to check. Attention to this detail was one of the things required to boost rs232 performance from 300 baud to 19200 baud on a 5MHz 8088. >on its own. Some day some naive person will write a driver for a new device >and not realize that he has to deal with this. I am perfectly happy to move It only took me 2 days to realize in my 1st PC hardware driver, for rs232 4 years ago :-). D
ast@cs.vu.nl (Andy Tanenbaum) (01/26/89)
In article <1480@ditsyda.oz> evans@ditsyda.oz (Bruce Evans) writes: >Interrupt() must be kept short since it runs with interrupts disabled. So I >don't want to have any port_out()s in it (a dozen or so instructions in C >compared with 2 in assembler) or lots of cases to check. Attention to this >detail was one of the things required to boost rs232 performance from 300 >baud to 19200 baud on a 5MHz 8088. Good point. Perhaps it is indeed best to move all the EOI stuff to the tasks instead of the interrupt handlers, since the tasks an run with interrupts enabled. This still leaves us with the question of how to exit from the interrupt handler and not get another interrupt instantly. Whatever solution is chosen should be efficient. Doing a port_out to the 8259 to change the mask is as expensive as doing a port_out to the 8259 for EOI. Andy Tanenbaum (ast@cs.vu.nl)
evans@ditsyda.oz (Bruce Evans) (01/28/89)
in article <1975@ast.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) says: > This still leaves us with the question of how to exit from the interrupt > handler and not get another interrupt instantly. Whatever solution is There is an amazingly simple answer. Do nothing. The reason we get another interrupt now is that the EOI done in interrupt() incorrectly tells the 8259 that the interrupt has been handled. Look at the PC BIOS keyboard driver. The first thing it does is an STI which is analogous to the task switch or return done at the end interrupt(). I think most of the BIOS drivers do this. I implemented this for my PC and 386-AT today. Of course an EOI must still be done in the high level task. It must now be a specific EOI: port_out(int_ctl_port, SPECIFIC_EOI | ((1 << irq_number) & 0x07)); where int_ctl_port is the interrupt control port (usually a constant for each driver, e.g., INT_CTL) SPECIFIC_EOI is 0x60 irq_number is the interrupt number (not vector), i.e., 0 to 15, 8 on each controller, e.g., 5 for XT_WINI, 6 for FLOPPY. I put these after all the fdc_results() and win_results() calls (actually in a new function) for the disk drivers. The sense command really belongs in the results function too. I'm using the 1.4a floppy driver.It works well. Perhaps the wini drivers will need a sense command for PS/2's too. One thing I didn't nail down is the PS/2's interrupt controller. There is a define for it as 0x3C in the assembler code only. Is this an 8259 with a mask port at 0x3D? The other drivers needed essentially no changes. The centralized EOI had to be deleted (lest the disks do it) so all the other drivers have to do it explicitly. It doesn't matter if it is done before or after device service, since all non-disk drivers run with interrupts disabled and the 8259 does not latch changes. (I'm fairly sure but don't believe me for level sensitive interrupts without trying it :-).) The clock is a special case in this. I actually use a low level handler, but since the clock has no device registers, the hardware part of the service routine is null and an immediate EOI does the job. There may be one minor problem :-). The 8259 enforces a priority scheme so while the XT_WINI interrupt is being handled at level 5, the FLOPPY interrupt at level 6 is locked out. These 2 priorities are well chosen and cause no trouble. The keyboard and rs232 priorities at 1 and 3-4 are not well chosen but cause no trouble because the devices are serviced with interrupts disabled anyway. I'm worried about the AT_WINI interrupt at level 14. The catch is that it is chained through level 2 on the first controller. So the 8259 is after all fairly well matched to Minix's interrupt handling. The main thing that is missing is a mode with no priorities. > chosen should be efficient. Doing a port_out to the 8259 to change the > mask is as expensive as doing a port_out to the 8259 for EOI. It is far worse than that. I implemented this mask fiddling approach too. It requires a port_in to get the current state, then a lock/restore to protect everything. 5 instructions and 8 bytes in assembler, but about 50 instructions in C. It still has to do an EOI as well. I did it in assembler to set the mask, in between the save() and interrupt() calls, and in C for the task level code to clear the mask. I think that the lock/restore approach is wasteful because the interrupt state is always known at the task level. I'm now investigating the "Special Mask Mode" which offers some hope of improving one or both methods. The documentation (data sheet) is so obscure that I'm using experimental methods. Whatever I end up with will be in the 386 protected mode version to be posted in a week or two (remember it will also run on PC's and provide other improvements). I already had a lot of #define's for the interrupt controller (to reprogram it at the start and to properly distinguish between interrupt numbers and interrupt vectors), and these could be used for the PS/2 code to avoid unnecessary diffs. Bruce Evans evans@ditsyda.oz.au
evans@ditsyda.oz (Bruce Evans) (01/30/89)
in article <1701@ditsyda.oz>, I said: > > in article <1975@ast.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) says: >> This still leaves us with the question of how to exit from the interrupt >> handler and not get another interrupt instantly. Whatever solution is > > There is an amazingly simple answer. Do nothing. The reason we get another > [...] > Of course an EOI must still be done in the high level task. It must now be > a specific EOI: > > port_out(int_ctl_port, SPECIFIC_EOI | ((1 << irq_number) & 0x07)); Sorry, (1 << irq_number) should be just irq_number. The bit power is only appropiate for adjusting the mask bits. > interrupts disabled anyway. I'm worried about the AT_WINI interrupt at > level 14. The catch is that it is chained through level 2 on the first > controller. The problem is sidestepped by doing an EOI for level 2 in the low level interrupt handler, leaving only the EOI for level 14 for the high level task. This works well with the current devices (tested reading the hard disk, the floppy, and doing rs232 concurrently. The printer at level 7 might not get much service with this activity). If future device drivers are written for levels 8 to 15 (all cascading through 2), this may be inadequate. So I am leaning towards the slightly more complicated but more flexible method of fiddling the mask bits. Bruce Evans evans@ditsyda.oz.au
ast@cs.vu.nl (Andy Tanenbaum) (01/31/89)
In article <1701@ditsyda.oz> evans@ditsyda.oz (Bruce Evans) writes: >There is an amazingly simple answer. Do nothing. I am not sure I follow. Suppose we just remove the sending of the EOI from interrupt(). Does this block all subsequent interrupts? Just those on the same interrupt line? I thought that Steve Ackerman did his mask fiddling because if he didn't, he got another interrupt immediately upon exiting the interrupt handler? Steve-- correct me if I am wrong. >Of course an EOI must still be done in the high level task. It must now be >a specific EOI: Why does it have to be specific now? Andy Tanenbaum (ast@cs.vu.nl)
evans@ditsyda.oz (Bruce Evans) (02/02/89)
in article <1988@ast.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) says: > > In article <1701@ditsyda.oz> evans@ditsyda.oz (Bruce Evans) writes: >>There is an amazingly simple answer. Do nothing. > I am not sure I follow. Suppose we just remove the sending of the EOI > from interrupt(). Does this block all subsequent interrupts? Just those It blocks those on the current line and lower priority lines (== higher interrupt numbers within the same 8259). I think Steve Ackerman needed the mask fiddling because he did not remove the EOI. >>Of course an EOI must still be done in the high level task. It must now be >>a specific EOI: > Why does it have to be specific now? The generic EOI applies to the last in-service line (or maybe the highest priority line - they are usually the same). The difference is like the one between function returns and coroutine returns. I used the "simple" method for a while but now prefer the mask fiddling approach. The simple method depends too much on the native privilege levels and may cause trouble in future. Currently it may cause unnecessary (but probably negligible) delays in servicing floppy and printer interrupts. I changed the wini drivers (back) to use mask fiddling but the floppy driver still uses the simple method. They are compatible once the centralized EOI is removed. The overheads for the mask fiddling are just: (1) Every interrupt handler (i.e. the part running with processor interrupts disabled) which completely services its device must do an EOI at its start or finish. This takes just 2 instructions each in assembler. The only devices not covered by this are the disks. Here's what it looks like for tty. --- _tty_int: | Interrupt routine for terminal input. call save | save the machine state call _keyboard | process a keyboard interrupt movb al,#ENABLE | reenable int controller out INT_CTL jmp _restart | continue execution --- (2) Other handlers must set a mask bit in the interrupt handler as well. This takes 3 instructions in assembler. The high level handler must clear the mask bit every time it clears the device interrupt and expects another. Here's the wini code (slightly edited, may not run :-)). This would be cleaner if the XT and AT routines were separated and the interrupt vector initialized to the appropiate one by a decision in main(). --- _wini_int: | Interrupt routine for the winchester disk. call save | save the machine state mov ax,_pc_at | check for 2nd int controller on AT test ax,ax jnz at_wini_int xt_wini_int: in INT_CTLMASK | mask further wini interrupts using 8259 orb al,#XT_WINI_BITPOWER | 0x20 out INT_CTLMASK movb al,#ENABLE | reenable all unmasked 8259 lines out INT_CTL jmp common_wini_int at_wini_int: in INT2_MASK | mask further wini interrupts using 8259 orb al,#AT_WINI_BITPOWER | 0x40 out INT2_MASK movb al,#ENABLE | reenable both 8259's (all unmasked lines) out INT2_CTL out INT_CTL | CONNECT_IRQ is on this common_wini_int: mov _int_mess+2,*DISKINT| build message for winchester task mov ax,#_int_mess | prepare to call interrupt(WINI, &intmess) push ax | push second parameter mov ax,*WINI | prepare to push first parameter push ax | push first parameter call _interrupt | this is the call jmp _restart | continue execution --- This is the high level code for the AT. It is exactly what I'm using. --- PRIVATE int win_results() { /* Extract results from the controller after an operation, then reenable the * (slave) interrupt controller. */ int old_mask; int old_state; int r; r = old_win_results(); old_state = lock(); port_in(INT2_MASK, &old_mask); port_out(INT2_MASK, old_mask & ~(1 << (AT_WINI_IRQ & 0x07))); restore(old_state); return(r); } --- Bruce Evans evans@ditsyda.oz.au