[comp.os.minix] Help! Minix 1.3 & PS/2 Model 80

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