Page 3 of 3

Re: Real Mode Confusion

Posted: Sun Oct 30, 2011 4:58 am
by Chandra
mitikoro wrote:Try moving the lidt instruction to 32-bit PM, just after the cli opcode.
Not a good suggestion.
Intel's Manual wrote:The processor reenters real-address mode if software clears the PE bit in
CR0 with a MOV to CR0 instruction. A procedure that attempts to do this,
however, should proceed as follows:

1. If paging is enabled, perform the following sequence:

þ Transfer control to linear addresses that have an identity mapping;
i.e., linear addresses equal physical addresses.

þ Clear the PG bit in CR0.

þ Move zeros to CR3 to clear out the paging cache.

2. Transfer control to a segment that has a limit of 64K (FFFFH). This
loads the CS register with the limit it needs to have in real mode.

3. Load segment registers SS, DS, ES, FS, and GS with a selector that
points to a descriptor containing the following values, which are
appropriate to real mode:

þ Limit = 64K (FFFFH)
þ Byte granular (G = 0)
þ Expand up (E = 0)
þ Writable (W = 1)
þ Present (P = 1)
þ Base = any value

4. Disable interrupts. A CLI instruction disables INTR interrupts. NMIs
can be disabled with external circuitry.

5. Clear the PE bit.

6. Jump to the real mode code to be executed using a far JMP. This
action flushes the instruction queue and puts appropriate values in
the access rights of the CS register.

7. Use the LIDT instruction to load the base and limit of the real-mode
interrupt vector table.


8. Enable interrupts.

9. Load the segment registers as needed by the real-mode code.

Re: Real Mode Confusion

Posted: Sun Oct 30, 2011 12:19 pm
by mitikoro
Well, my own code works fine when I load the real mode IDTR from protected mode. In fact, the LIDT opcode description in the manual says that the only change is how many bytes are fetched from the pointer (5 bytes in 16 bit mode.)

Re: Real Mode Confusion

Posted: Sun Oct 30, 2011 10:35 pm
by RobertF
Intel's Manual wrote: 2. Transfer control to a segment that has a limit of 64K (FFFFH). This
loads the CS register with the limit it needs to have in real mode.
This is for the jump to real mode, right? Everything I've seen (including the wiki page) has noted to use a limit of 1MB, not 64K. Would this even cause a huge problem in a code?

Re: Real Mode Confusion

Posted: Mon Oct 31, 2011 12:26 am
by Chandra
RobertF wrote:This is for the jump to real mode, right? Everything I've seen (including the wiki page) has noted to use a limit of 1MB, not 64K. Would this even cause a huge problem in a code?
It won't, as far as I know. Depending upon your implementation of gdt_set_gate(...), the higher 4 bits of the limit are shifted to the granularity field so the limit, as defined in the GDT structure, would still be 64 kb whereas the segment access would actually be allowed upto 1 Megabytes. Besides, the segment registers will only allow you to access the 64 kb window(16-bit) at once.

Re: Real Mode Confusion

Posted: Mon Oct 31, 2011 6:36 pm
by RobertF
AFAIK, int works like a far jump. After I run my code and shut down the system, CS is 1h, which I'm assuming means int never returned to my code. I'll get a LOCK prefix error and "read from port 0 with length 2 returns 0xFFFF" if I set the stack the way I have been.

However, if I manually set SP and SS (to C00h and 1000h accordingly), I don't get any errors, but CS still stays stuck at 1h. I'm not sure if this is a good thing or if it just means I'm not even setting up a stack.

Bochs Log:

Code: Select all

01488760000i[CPU0 ] CPU is in real mode (active)
01488760000i[CPU0 ] CS.d_b = 16 bit
01488760000i[CPU0 ] SS.d_b = 16 bit
01488760000i[CPU0 ] | EAX=0000ec5c  EBX=0002c560  ECX=00000079  EDX=00120000
01488760000i[CPU0 ] | ESP=001162e2  EBP=0011c008  ESI=0002c6db  EDI=0002c6dc
01488760000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf
01488760000i[CPU0 ] | SEG selector     base    limit G D
01488760000i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
01488760000i[CPU0 ] |  CS:0001( 0003| 0|  0) 00000010 000fffff 0 0
01488760000i[CPU0 ] |  DS:0000( 0004| 0|  0) 00000000 000fffff 0 0
01488760000i[CPU0 ] |  SS:0c00( 0004| 0|  0) 0000c000 000fffff 0 0
01488760000i[CPU0 ] |  ES:0000( 0004| 0|  0) 00000000 000fffff 0 0
01488760000i[CPU0 ] |  FS:0000( 0004| 0|  0) 00000000 000fffff 0 0
01488760000i[CPU0 ] |  GS:0000( 0004| 0|  0) 00000000 000fffff 0 0
01488760000i[CPU0 ] | EIP=000020ba (000020ba)
01488760000i[CPU0 ] | CR0=0x60000010 CR2=0x00000000
01488760000i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
01488760000i[CPU0 ] 0x000020ba>> mov word ptr ds:[bx+si], ax : 8900
Would bochs tell me if there was any hardware with settings that it doesn't want for real mode? (It has before, just curious if I missed something.) At this point, is it probably something in my kernel screwing this up or should I continue tweaking the code? Or is this a good time to give up? :P

Re: Real Mode Confusion

Posted: Tue Nov 01, 2011 5:03 am
by mitikoro
In your dump, ESP isn't 0x1000. Set the stack like this:

Code: Select all

[BITS 16]
;; Real mode
mov ax, 0xC00
mov ss, ax
mov esp, 0x1000
Check your stack address so it doesn't overwrite anything. Also, set your PM stack to a BSS buffer in your kernel, like:

Code: Select all

[BITS 32]
;; PM init code
mov esp, StackTop
call _main
...
[SECTION .bss]
resb 1024   ;; 1024 bytes of PM stack
StackTop:
How are you loading your kernel? Are you using GRUB or your own bootloader? If you are using your own bootloader, is your kernel loaded correctly (fully loaded, no bad sectors, etc)?

Is the GDT being set correctly? Make sure your gdt_set_gate function is correct (and if you are using C structs for the GDT entries, make sure they aren't aligned by the compiler)
Are you still moving the IVT to 0x4000? If some other code is overwriting the IVT, fix that code, but don't move the IVT.
Is your other code changing the hardware config (like PIC config), modifying BIOS structures (like the IVT) or affecting this code in any way? If that is true, we can't help you.

Place infinite loops along your code (jmp $). Use the Bochs debugger and the magic breakpoint (xchg bx, bx).
Check that register values are correct in each infinite loop/breakpoint. If you are using the debugger, step through the code, printing registers as you advance.

And don't give up: debugging is fun. There's nothing like the feeling of accomplishment when you finally correct a nasty bug in the code 8)

Re: Real Mode Confusion

Posted: Tue Nov 01, 2011 8:56 am
by RobertF
mitikoro wrote:In your dump, ESP isn't 0x1000.
Even when I set the stack using ESP, after I call int, ESP changes to a value above 0x1000 (0x713E). Strange. (Regardless, it's changed to esp now)
mitikoro wrote:How are you loading your kernel? Are you using GRUB or your own bootloader? If you are using your own bootloader, is your kernel loaded correctly (fully loaded, no bad sectors, etc)?
It's GRUB, so it should be okay.
mitikoro wrote:Is the GDT being set correctly? Make sure your gdt_set_gate function is correct (and if you are using C structs for the GDT entries, make sure they aren't aligned by the compiler)
To make sure it worked correctly, I created/loaded a new GDT inside the real mode program, and with the same result, I assume gdt_set_gate was working fine (I had checked it as well, though).
mitikoro wrote:Are you still moving the IVT to 0x4000? If some other code is overwriting the IVT, fix that code, but don't move the IVT.
I checked the IVT before I executed the program, and just want to make sure this is normal:

Code: Select all

Index    Segment    Offset
0          1        0x4000
1          1        0x5000
2          1        0x6000
...
11         1        0xF000
12         2        0x0000
13         3        0x1000
It just goes on from there.
mitikoro wrote:Check that register values are correct in each infinite loop/breakpoint. If you are using the debugger, step through the code, printing registers as you advance.
I've been having trouble getting the debugger to work—all I got working was gdb, which didn't have the sregs command you mentioned earlier. I guess I'll have to quit putting that off, then.

Re: Real Mode Confusion

Posted: Tue Nov 01, 2011 9:43 am
by mitikoro
I've been having trouble getting the debugger to work—all I got working was gdb, which didn't have the sregs command you mentioned earlier. I guess I'll have to quit putting that off, then.
If you are using Windows (and using the precompiled binaries), the Bochs debugger is called bochsdbg.exe. It's in the same folder as bochs.exe.
If you are using Linux, check that your distro provides the bochs debugger in some package. If not, you'll have to recompile Bochs. You have to add options --enable-debugger, --enable-disasm, etc. to the configure script.
To enable magic breakpoints, add this to your bochsrc file:

Code: Select all

magic_break: enabled=1
How are you loading your kernel?
It's GRUB, so it should be okay.
IIRC GRUB can't load anything below 1MB, yet your code uses ORG 0x7E00. How is that? Also, ORG is not valid for PE, ELF or other executable formats, only binary. Are you directly copying the code to phys. addr 0x7E00? If that is the case, all far jumps will be invalid, if you haven't fixed them (and all absolute addresses).

As for the IVT dump, I am confused. Is that what is present at the location that is pointed by the IDTR? IIRC these addresses don't lie in ROM space. They're pointing at empty RAM. You're likely to execute bogus code if you call any BIOS int with those vectors. If you are still copying the IVT to another address, look for bugs in the copy code. Also look for anything that may be writing over the IVT in your entire kernel.

Re: Real Mode Confusion

Posted: Tue Nov 01, 2011 10:41 am
by RobertF
IIRC GRUB can't load anything below 1MB, yet your code uses ORG 0x7E00. How is that? Also, ORG is not valid for PE, ELF or other executable formats, only binary. Are you directly copying the code to phys. addr 0x7E00? If that is the case, all far jumps will be invalid, if you haven't fixed them (and all absolute addresses).


At one point I thought compiling it as a flat binary would make a difference, which is why I used ORG. Now I'm back to ELF, but the start address is still 0x7E00. I'm reading the files from the floppy and copying the code to memory. If GRUB can't load anything below 1MB, and real mode requires the program to be under 1MB, will this just not work?

As for the IVT dump, I am confused. Is that what is present at the location that is pointed by the IDTR? IIRC these addresses don't lie in ROM space. They're pointing at empty RAM. You're likely to execute bogus code if you call any BIOS int with those vectors. If you are still copying the IVT to another address, look for bugs in the copy code. Also look for anything that may be writing over the IVT in your entire kernel.
I'm not copying the IVT anymore; what I got was from me reading the first 0x400 bytes from a pointer to 0x0. I'll look through the kernel and see if I'm overwriting anything (I found a snippet that was before and removed it; guess I missed something)

Re: Real Mode Confusion

Posted: Tue Nov 01, 2011 4:51 pm
by RobertF
Finally got it to work. Something in my kernel is overwriting the IVT. For a temporary fix, I saved the IVT at start and moved it back to where it should be right before execution.