Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
__shutdown:
cli ; disable interrupts
;------ clear the PG bit ------
mov eax, cr0
xor eax, 0x80000000
mov cr0, eax
;------ clear the paging cache ------
xor eax, eax
mov cr3, eax
;------ do a far jump to a 16-bit code segment ------
jmp dword real_mode
[BITS 16]
real_mode:
;------ clear the PE bit ------
mov eax, cr0
xor eax, 1
mov cr0, eax
;------ do a far jump ------
jmp dword real2
real2:
hlt ; halts the CPU later it will really shutdown
jmp real2
You must clear the PM bit (bit 0 of cr0) to go back from protected mode.
Maybe you should use a "and" bitmask rather than a "xor", to ensure the bits are correctly cleared
I hate to say this but it seems you are doing it all wrong.
I have written code that returns to real mode. IIRC this is what I did:
1) Setup a 16 bit protected mode code descriptor in your GDT, and ensure the page is identity mapped. You may also need a 16 bit data descriptor, I can't remember
2) Jump to 16 bit protected mode using a far jump
3) Turn off paging (reload CR3 to flush pages then clear PG bit)
4) Turn off protected mode (clear PE bit)
5) Far jump to 16 bit real mode code
6) Set DS etc
Also it is described in the Intel manuals how to return to real mode. Go read them.
i read it but it doesn't work, see the code at the top of that thread, it makes the things in steps like the intel manuel describes, without setting up stack etc.
1. How can I setup a GDT entry while I'm in pmode? For example if the bootloader which load my kernel don't do this.
2. I think I do a far jump (jmp 'dword' ...) or ??? If I write jmp far ... nasm outs: "error: a.out format does not support segment base references"
3. The intel manuel says: clear PG bit of cr0 than clear cr3
you're not doing a far jump: just a long jump ...
You should try harder to have the far jump encoded by NASM, (should be jmp far SELECTOR:dword offset)
the GDT setup process is exactly the same when you're in rmode or pmode: fill your GDT, write its *linear* address and size in a chunk of memory that you'll load with LDGT [...]
short comment on the short-sightedness of some people in this thread, you don't need a new GDT entry. The point of making a new 16-bit segment, jumping to that, and then continuing with going to realmode in that segment is that you don't have to worry about prefixing properly. The point is, you should *always* prefix properly. Just like you can jump from 16-bit realmode to 32-bit protected mode (which even Intel and AMD don't seem to understand), you can jump from 32-bit protected mode back to 16-bit realmode. Just make sure you do a 66 EA <offset> <segment> and nothing else. Note also both the offset and segment are 16-bit.
If you like that more, you can always opt for the 16-bit code. If you want to be /REALLY/ sure you don't mess up, try taking the 512th GDT entry for the 16-bit code and identity-map it to 0x10000. This way, even the segments remain the same (of course, the hidden parts don't, so you need a reload anyway).
How can I set the cs register to 16 Bit? If I only jump the cs register is still 32 bit. And it have to be 16 if I want use interrupts. And what else should I do?
ich_will wrote:
1. What do you mean with "66 EA <offset> <segment>"?
That the jump, after being assembled, should look like 0110011011101010xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy as a bit pattern, or 66EAxxxxyyyy as nibble pattern. xxxx == offset, yyyy == segment. At the moment it probably assembles to 11101001xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (E9xxxxxxxx)which makes for an in-segment jump. One that doesn't change the bit size for instance.
2. If I understood all right, I only have to jump to an 16 Bit
codesegment like:
How can I set the cs register to 16 Bit? If I only jump the cs register is still 32 bit. And it have to be 16 if I want use interrupts. And what else should I do?
The CS register is 16-bits all the time. The descriptor loaded in the CS register is always 64-bits. The segment pointed to by the descriptor loaded in the CS register can be 32 or 16 bits (or 64-bits on amd64). By reloading the CS register, and you do that by jumping with a far jump (encoded as EA), which in the case of a jump to a 16-bit segment should be prefixed with 66, and in the case of a jump to a 32-bit segment should be prefixed with 66, and must be prefixed with 66 if you want to jump beyond 0xFFFF.