Page 1 of 1

No INTs after switch to real mode

Posted: Tue Sep 02, 2008 10:45 am
by inflater
Hi,
if I would switch from protected mode to real mode, it all works, but I can't receive interrupts - e.g. xor ax, ax int 16h will freeze with "internal keybuffer full, ignoring scancode", that same applies if I would try to read from floppy drive using BIOS's services.

My kernel starts from real mode and there I save the original GDT and IDT,

Code: Select all

savegdt:
rw 1
rd 1

saveidt:
rw 1
rd 1

sgdt [savegdt]
sidt [saveidt]
and then, immediately before the rmode switch, I load these backup gdt and idt, lgdt [savegdt], lidt [saveidt]. After switching I set the segment registers and remap the PIC to real mode values, like this:

Code: Select all

remap_pic_realmode:
	cli
	out 0x21,al
	mov al,0x4
	out 0x21,al
	mov al,0x1
	out 0x21,al
	mov al,0x11
	out 0xa0,al
	mov al,0x70
	out 0xa1,al
	mov al,0x2
	out 0xa1,al
	mov al,0x1
	out 0xa1,al
	mov al,0x0
	out 0x21,al
	mov al,0x0
	out 0xa1,al
	sti
	ret
but it doesn't work either.

My guess is that the PIC is remapped incorrectly... any advice?

Regards
inflater

Re: No INTs after switch to real mode

Posted: Tue Sep 02, 2008 2:02 pm
by Combuster
I miss the delays [/pendant]
But that shouldn't break bochs... Are there pending interrupts coming up at times when you can't use them?

Re: No INTs after switch to real mode

Posted: Tue Sep 02, 2008 10:37 pm
by inflater
Well, I tried to read from floppy using bios' int 13h using these functions and Bochs just stuck on a loop in ROM BIOS:

Code: Select all

f000:05f4 (unk. ctxt): push bp
f000:05f5 (unk. ctxt): mov bp, sp
f000:05f7 (unk. ctxt): push bx
f000:05f8 (unk. ctxt): push ds
f000:05f9 (unk. ctxt): mov ax, word ptr ss:[bp+0x4]
f000:05fc (unk. ctxt): mov ds, ax
f000:05fe (unk. ctxt): mov bx, word ptr ss:[bp+0x6]
f000:0601 (unk. ctxt): mov al, byte ptr ds:[bx]
f000:0603 (unk. ctxt): pop ds
f000:0604 (unk. ctxt): pop bx
f000:0605 (unk. ctxt): pop bp
f000:0606 (unk. ctxt): ret
f000:94b4 (unk. ctxt): add sp, 0x0004
f000:94b7 (unk. ctxt): and al, 0x80
f000:94b9 (unk. ctxt): mov byte ptr ss:[bp+0xfff1], al
f000:94bc (unk. ctxt): mov al, byte ptr ss:[bp+0xfff1]
f000:94bf (unk. ctxt): test al, al
f000:94c1 (unk. ctxt): jz .+0xffa1 (0x000f9464)
f000:9464 (unk. ctxt): mov ax, 0x0040
f000:9467 (unk. ctxt): push ax
f000:9468 (unk. ctxt): mov ax, 0x0040
f000:946b (unk. ctxt): push ax
f000:946c (unk. ctxt): call .+0x7185 (0x000f05f4) - it calls f000:05f4 again
I think it wants to read from something. Judging from "mov ax,0040", "push ax", "mov ax,word ptr ss[bp]" it wants to access the rom bios reserved RAM but its stuck in a infinite loop because some value is not as expected. It waits for a interrupt to fire I think...

What was weird, on real machine (3.2GHz) it starts after 10 seconds, but the floppy motor was still on (and aftermath floppy reading resulted in failure). On Bochs it just got stuck.
Are there pending interrupts coming up at times when you can't use them?
Shouldn't be, but is it just enough loading the previously loaded GDT and IDT from real mode and then remaping the PIC? I think the PIC is remapped bad, or there must be something else to be done.

BTW the whole procedure runs in a OS interrupt ISR. It does mask all irqs when switching to rmode and then unmasks it by remapping the PIC, of course with STI after. Then it saves all registers from pmode, and loads it to the BIOS int. After bios int finishes, it masks all irqs, returns to pmode and remaps the PIC for pmode.
But it haves the same result if I replace the calling for this ISR, by a FASM macro to switch to rmode and back. It haves exactly the same result - ints not firing I presume.

This routine works when a interrupt is not requested (for example, bios video services, FDD drive reset etc), but it does hang on even "wait for key" bios function, INT 16h with AX=0.

So the thing doesn't receive interrupts. Any more advices?

//EDIT: In the asm file there are both macros to switch to pmode and back... I think they shouldn't be incorrect. "InitializePmode" is called only once, and on the beginning in my kernel.

Re: No INTs after switch to real mode

Posted: Wed Sep 03, 2008 12:23 am
by Brendan
Hi,

Some notes..

1) you don't need to save the real mode GDT when you switch to protected mode or reload the real mode GDT when you switch back to real mode, because real mode doesn't use the GDT to start with. In some (rare) situations you might need to reload the GDT when you switch back to protected mode in case some real mode code trashed it.

2) When you're switching back to real mode, you need to load SS with a 16-bit descriptor before clearing the PE flag in CR0. Otherwise you end up in real mode with a 32-bit stack, where things like "push byte 0x12" and "push ax" will push 4 bytes onto the stack and stuff up code that passes parameters on the stack because the parameters are at the wrong place and stack cleanup (e.g. "add sp, number_of_paremeters * 2") doesn't work.

3) If you need to reprogram the PIC chips while the OS is running then you risk losing IRQs (especially pending IRQs), and your OS may lock up because it's waiting for an IRQ that you lost or because the PIC is waiting for an EOI that you don't know you need to send. Basically there's no way to atomically switch the PIC chips from one configuration to another without losing "PIC chip state", and no way to save the PIC chip state (e.g IRR and ISR) and restore them later without having race conditions (e.g. PIC chip receives IRQ from device at wrong time). The CLI instruction won't help because that doesn't prevent devices from sending the IRQs to the PIC chip. Masking all the IRQs in the PIC won't help either (you're guanteed to lose IRQs that way). This can also be extremely painful bug to fix later if you're not aware of it - an OS that usually works but crashes at random times with no way to trigger the bug to see where it is and no exception to tell you where to start looking.

3a) The only way to do this properly is to leave the PIC chip configuration alone. For example, for each IRQ install a real mode IRQ handler in the IVT that sets an "IRQ occured" flag, and then fake the IRQ/s when you return to protected mode (using an "INT n" instruction for each "IRQ occured" flag that was set while you were in real mode). Of course for devices that are being handled the BIOS in real mode you'd skip this (for e.g. if hard drive access is always done via. the BIOS then you'd leave IRQ 14 and IRQ 15 alone).

3b) You could still reprogram the PIC chip during boot, so that (for e.g.) IRQ 0 causes interrupt 0x90 and IRQ 8 causes interrupt 0x98. This avoids the need to have IRQs mapped to the interrupt vectors used for exception handlers in protected mode. In this case you'd need real mode IRQ handlers that set the "IRQ occured" flag like before, except that any IRQs that the BIOS is using need to be sent back to the BIOS's original interrupt handler (e.g. hard drive IRQ 14 causes interrupt 0x9E which starts your real mode interrupt handler, where your real mode interrupt handler does a "JMP far [IVT+vector*4]").

3c) If you ever want to support I/O APICs you still might be able to do this. For example, hard drive IRQ causes interrupt 0xAE which starts your real mode interrupt handler, where your real mode interrupt handler pushes some fake return information on the stack (so the BIOS's IRET returns to your code) then does a "JMP far [IVT+vector*4]" to the BIOS's interrupt handler (which send it's useless EOI to the unused PIC chip) and returns to your code, which sends the EOI to the I/O APIC. I'd expect some hassles with this though (it should work for ISA IRQs, but I'd expect problems getting it to work with PCI IRQs). For SMP you'd also want to make sure all IRQs that the BIOS uses are sent to the BSP (so you don't have several CPUs trying to execute non-reentrant BIOS code at the same time).

3d) The BIOS is crap, in that a multi-tasking OS should be able to multi-task, and shouldn't need to waste millions of cycles waiting for a single-tasking BIOS function to receive an IRQ.

3e) If you don't care about losing IRQs and performance (e.g. you're only using this code during boot, before native device drivers are started) then IMHO you've switched to protected mode too early. This seems to be a relatively common design flaw (potentially inspired by HLL compilers), but it's like putting your shoes on then trying to find a way to get your socks on. ;)


Cheers,

Brendan

Re: No INTs after switch to real mode

Posted: Wed Sep 03, 2008 3:46 am
by egos
inflater wrote:but it doesn't work either.

Code: Select all

        mov     bx, 7008h ; The default base indexes are 8 for Master PIC and 0x70 for Slave PIC !!!
        mov     dx, [savedirqmask]

        mov     al, 00010001b
        out     20h, al
        out     0A0h, al

        mov     al, bl
        out     21h, al
        mov     al, bh
        out     0A1h, al

        mov     al, 00000100b
        out     21h, al
        mov     al, 02h
        out     0A1h, al

        mov     al, 00001101b
        out     21h, al
        mov     al, 00001001b
        out     0A1h, al

        mov     al, dl
        out     21h, al
        mov     al, dh
        out     0A1h, al

Re: No INTs after switch to real mode

Posted: Wed Sep 03, 2008 4:52 am
by inflater
Thanks for the explanation Brendan. Actually, I use to-real-mode switching e.g. when changing video modes, or a legacy FDD driver, because I've found that my own does not work properly on all machines (some emulators, rHW) and its way too slow, even slower than actual pmode to rmode switching (my driver reads one sector and does a 500ms pause lol... dunno why), so I'm providng a alternate solution.

The above code partially works, it throws some errors in Bochs regarding "ICW1: single mode not supported", but when clicked Continue four times, the floppy reads succeed. (VirtualBox crashs instantly.) I'll ltry to fix the panic messages.

//EDIT: All fixed and working. Wasn't that hard...
Huge speed improvement with the 16-bit driver, especially in VirtualBox and possibly some rHW.
Bochs has the same speed as with the 32-bit driver.