Page 1 of 2

Wrote a tutorial covering long mode

Posted: Sun Feb 21, 2010 3:52 am
by StephanvanSchaik
Hi,

I wrote a new article on the wiki today which covers setting up long mode. Any suggestions are welcome as well as making it more conform to the wiki itself (If somebody likes to). You can find the article here. I hope it will be of any use.


Regards,
Stephan J.R. van Schaik.

Re: Wrote a tutorial covering long mode

Posted: Sun Feb 21, 2010 6:03 am
by xenos
First of all, this is absolutely great work :) I guess it will help many people fiddling around with the transition to long mode.

Second, I have some small hints. At the very end of the article, you clear the screen using "rep movsq". I guess you should set RDI to the start of video ram before doing so. In the same piece of code, instead of the power-consuming endless loop at the end, I would recommend to halt the CPU, so beginners who just copy and paste the article into some assembler won't heat up their CPUs too much. Finally, the binary numbers in your code are really nice as one can simply count which bit is set / cleared, but I would add some comment like "set bit 23", just to make things clear.

Re: Wrote a tutorial covering long mode

Posted: Sun Feb 21, 2010 8:36 am
by quanganht
I have an idea on the CPUID part. You don't have to worry weather the CPU is from AMD or Intel. An piece of code below with detect 64-bit capability. And 64-bit supported means PAE supported, so you don't have to check for PAE if the CPU is 64-ready

Code: Select all

mov eax, 80000000h  ; Extended-function 8000000h.
cpuid              ; Is largest extended function
cmp eax, 80000000h  ; any function > 80000000h?
jbe no_long_mode     ; If not, no long mode.
mov eax, 80000001h ; Extended-function 8000001h.
cpuid               ; Now EDX = extended-features flags.
bt edx, 29      ; Test if long mode is supported.
jnc no_long_mode      ; Exit if not supported.
Just in case you want to know, this code is taken from AMD manual, Chapter 14 section 8 :wink:

Re: Wrote a tutorial covering long mode

Posted: Sun Feb 21, 2010 10:25 am
by StephanvanSchaik
XenOS wrote:First of all, this is absolutely great work :) I guess it will help many people fiddling around with the transition to long mode.

Second, I have some small hints. At the very end of the article, you clear the screen using "rep movsq". I guess you should set RDI to the start of video ram before doing so. In the same piece of code, instead of the power-consuming endless loop at the end, I would recommend to halt the CPU, so beginners who just copy and paste the article into some assembler won't heat up their CPUs too much. Finally, the binary numbers in your code are really nice as one can simply count which bit is set / cleared, but I would add some comment like "set bit 23", just to make things clear.
Thanks a lot. Yes, I should have set rdi to 0xB8000 #-o, I forgot about that. Will update the article.
quanganht wrote:I have an idea on the CPUID part. You don't have to worry weather the CPU is from AMD or Intel.
I was expecting other x86-like processors to have some other flags, if they even actually support those functions, but I guess not.
quanganht wrote:An piece of code below with detect 64-bit capability.And 64-bit supported means PAE supported, so you don't have to check for PAE if the CPU is 64-ready

Code: Select all

mov eax, 80000000h  ; Extended-function 8000000h.
cpuid              ; Is largest extended function
cmp eax, 80000000h  ; any function > 80000000h?
jbe no_long_mode     ; If not, no long mode.
mov eax, 80000001h ; Extended-function 8000001h.
cpuid               ; Now EDX = extended-features flags.
bt edx, 29      ; Test if long mode is supported.
jnc no_long_mode      ; Exit if not supported.
Just in case you want to know, this code is taken from AMD manual, Chapter 14 section 8 :wink:
I saw the code in the manual once, but generally derived it myself based on the ideas I had about enabling long mode. Thanks for the information.

Edit: updated it.


Regards,
Stephan J.R. van Schaik.

Re: Wrote a tutorial covering long mode

Posted: Mon Feb 22, 2010 8:09 am
by quanganht
What a great tutorial! Can you please continue with the IDT and interrupts ? (I won't be helpful as I found it a little confusing :( )

Re: Wrote a tutorial covering long mode

Posted: Mon Feb 22, 2010 11:26 am
by StephanvanSchaik
quanganht wrote:What a great tutorial! Can you please continue with the IDT and interrupts ? (I won't be helpful as I found it a little confusing :( )
Sure, basically the IDT used for 64-bit is the same as the one used for 32-bit though with generally two exceptions: the entries have been extended with an additional eight bytes (where the first four bytes serve as part of the offset) and the pointer has been extended to 80-bit as well (16-bit size, 64-bit base).


Regards,
Stephan J.R. van Schaik.

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 7:45 am
by GDTR
Thank for your work.

After entering long mode, U don't reload SS (mov ss,ax). It means that CPU continue using prot mode stack.
But when I try to set new LM stack, bochs fires SS exception, saying that bit P is not set.

I using GRUB2 -> 32bit stub in ELF64.

Should I setup 32bit gdt first, prior entering long mode?
Or this is some kind of bug in Bochs?

Any ideas?

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 8:33 am
by StephanvanSchaik
Hi,

The GDT used for entering long mode is actually temporary. You ought to set up another GDT afterwards. In my kernel the new GDT to be loaded looks like this:

Code: Select all

GDT:
    .Null:                              equ $ - GDT
                                        dq 0
    .KCode:                             equ $ - GDT
                                        dw 0
                                        dw 0
                                        db 0
                                        db 0x98
                                        db 0x20
                                        db 0
    .KData:                             equ $ - GDT
                                        dw 0
                                        dw 0
                                        db 0
                                        db 0x92
                                        db 0x20
                                        db 0
    .UCode:                             equ $ - GDT
                                        dw 0
                                        dw 0
                                        db 0
                                        db 0xF8
                                        db 0x20
                                        db 0
    .UData:                             equ $ - GDT
                                        dw 0
                                        dw 0
                                        db 0
                                        db 0xF2
                                        db 0x20
                                        db 0
    .Pointer:                           dw $ - GDT - 1
                                        dq GDT
And the way I load the GDT and set up the segment registers looks like this:

Code: Select all

    mov rsp, 0x90000
    lgdt [GDT.Pointer]
    push QWORD GDT.KData
    push QWORD 0x90000
    pushfq
    pushfq
    pop rax
    and rax, 1111111111111111111111111111111111111111111111101011111011111111b
    push rax
    popfq
    push QWORD GDT.KCode
    push QWORD .Flush
    iretq
    
.Flush:
    mov ax, GDT.KData
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
You might want to look at the AMD64 Architecture Programmer's Manual, Volume 2 to be more specific, as this manual contains more information about how the GDT should be laid out, but also about the EFLAGS I modified before using the IRETQ-instruction to reload the segment registers, more specifically the CS and SS registers.


Regards,
Stephan J.R. van Schaik.

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 5:31 pm
by gerryg400
Stephan,

Gotta congratulate you on your Long Mode tutorial. It is excellent. I have one possible correction, though I must admit I haven't actually tried your code.

Code: Select all

    ; Set the A-register to 0x00004000.
    or eax, 0x00004000
 
    ; Set control register 3 to the A-register.
    mov cr3, eax
Shouldn't the 'or' be a 'mov' ?

- gerryg400

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 5:43 pm
by Firestryke31
Not if he only wants to set that one bit. OR will leave all of the other bits alone, and so will preserve the other flags (such as paging enabled and whatnot).

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 6:17 pm
by gerryg400
CR3 holds an address (of pml4). Well it holds some bits in the lower 12 bits, but Stephan is not setting those. Fairly sure it sure be a 'mov'

- gerryg400

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 7:14 pm
by Firestryke31
Image

I was thinking CR4. My bad.

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 11:09 pm
by gerryg400
After entering long mode, U don't reload SS (mov ss,ax). It means that CPU continue using prot mode stack.
But when I try to set new LM stack, bochs fires SS exception, saying that bit P is not set.
GDTR,

These are my findings (mainly from trial and error because the manuals aren't always very descriptive.)

You don't need to load SS. It's difficult to understand (for me anyway) but in long mode there are no data/stack segments. It's just one big memory area.

If you want to load SS, load the NULL selector. Other values crash my machine. Not sure about yours.

Also you don't need to load DS or ES. If you do load them make sure the P bit in the selector is set. P not set crashes my machine. But the processor ignores every other part of a data descriptor. Even the DPL is ignored. You don't need to have separate RINGn data segments. You use the paging mechanism for protection.

As I said this information was gathered by trial and error. I would like to hear from anyone who has a different experience.

- gerryg400

Re: Wrote a tutorial covering long mode

Posted: Sun Apr 18, 2010 11:24 pm
by StephanvanSchaik
gerryg400 wrote:Stephan,

Gotta congratulate you on your Long Mode tutorial. It is excellent. I have one possible correction, though I must admit I haven't actually tried your code.

Code: Select all

    ; Set the A-register to 0x00004000.
    or eax, 0x00004000
 
    ; Set control register 3 to the A-register.
    mov cr3, eax
Shouldn't the 'or' be a 'mov' ?

- gerryg400
Yes, most likely. I don't have that revision of my source code any more so I'm not sure whether this is just me being confused with the OR and MOV instructions all around, or if it was something I got directly out of my source. In a newer version of my source I handle it like this (when creating the 32-bit page table):

Code: Select all

    mov edi, DWORD [FreeMemory]
    add edi, 0xFFF
    and edi, 11111111111111111111000000000000b
    mov cr3, edi
And in the 64-bit version I use:

Code: Select all

    mov edi, cr3

Regards,
Stephan J.R. van Schaik.

Re: Wrote a tutorial covering long mode

Posted: Mon Apr 19, 2010 6:51 am
by GDTR
Hi there, few question about tutorial here

Code: Select all

    mov di, 0x1000
    xor ax, ax
    mov cx, 16384
    rep stosb  ; Clear the memory.
After this part di points to 4096+16384, so we need to set it one more time: mov di, 0x1000

Code: Select all

    ; Set the word at the destination index to 0x2003.
    mov WORD [di], 0x2003
     ; Add 0x1000 to the destination index.
    add di, 0x1000
     ; Set the word at the destination index to 0x3003.
    mov WORD [di], 0x3003
     ; Add 0x1000 to the destination index.
    add di, 0x1000
     ; Set the word at the destination index to 0x4003.
    mov WORD [di], 0x4003
     ; Add 0x1000 to the destination index.
    add di, 0x1000
...and we dont need last add di, 0x1000

Next, we need to set di to 0x4000, not add 0x4000 to current value: mov di,0x4000

Code: Select all

    ; Set the destination index to 0x4000.
    add di, 0x4000
 
    ; Set the B-register to 0x00000003.
    mov ebx, 0x00000003
 
    ; Set the C-register to 512.
    mov cx, 512
Last note. We assume that ecx and edi (their high part) is zero, but it not always true.
mb use edi, ecx insted? (or zero them at begining)

UPD
AMD64 APM vol. 2, page 357
"- Data-segment descriptors for software running in compatibility mode. The DS, ES, and SS
segments are ignored in 64-bit mode.
See “Data-Segment Descriptors” on page 87 for more
information."

Best wishes,
Igor