Proctected mode and GDT question

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.
Post Reply
mihe
Member
Member
Posts: 38
Joined: Sun Oct 21, 2018 1:37 pm

Proctected mode and GDT question

Post by mihe »

Hello,

first post here :-)

First of all I would like to thank you guys for all documentation, tutorials and forum discussions of this site. It is a mine of gold and I am having a blast taking my first steps in the OS dev world.

I am trying to get a grasp on the concepts and techniques around memory while in protected mode.

I have been reading about DGT descriptor and entries, memory layout, meaning and purpose of each of the 8 bytes of every DGT entry, and how to switch to Protected mode.

I have been commenting and executing carefully using bosch a simple example I found that writes a string while in Real mode, using Int 0x10 and then switches to Protected mode, defines one code segment and one data segment (covering the whole 4 Gb each), and then switches to Protected mode and writes another string writing over the VGA RAM area.

Where I am getting lost is in this line:

jmp CODE_SEG:b32

CODE_SEG at that moment is equal to 0x08, which happens to be the GDT entry for the code section. My question/s are:
  • I fail to understand why using 0x08:b32 to jump to b32.
    What would be the difference of just jump to b32.
    Is this what we consider a far jump?
    If so, how b32 ended up in a different segment? Is there any relationship of this with the [bits 32] directive?
This is the code (Comments are mine)

Any tip, advise, suggestion will be very welcome !! Thanks in advance !

Code: Select all

; TODO: WRITE VGA RAM  IN REAL MODE!!!!!!!!!!! INVESTIGATE THE OFFSET OF THE MEMORY AND TRY RANDOM STUFF THERE 
[bits 16]
[org 0x7c00]
	xchg bx, bx ; magic breakpoint
    jmp 0:kernel_start

gdt_start:

gdt_null:
    dd 0x0 ; 0x00000000 double word, 32 bits, 4 bytes < GDT descriptor 2 + 4 bytes, size + offset
    dd 0x0 ; 0x00000000 double word, 32 bits, 4 bytes ; can be used for a pointer to GDT itself 

gdt_code: ;cs should point here, code segment
    dw 0xffff ; 0xFFFF	<- limit low	2 bytes word					0 - 63 (20 bits wide )
    dw 0x0 ; 0x0000		<- base low		2 bytes word
    db 0x0 ; 0x00		<- base middle	1 byte
    db 10011010b ; 0x9A <-  access 		1 byte - 1 fix - ring:0 - exec:true  -direction:down
    db 11001111b ; 0xCF <- granularity	1 byte - high = flags, low = limit high, for 4 bits for  20 bits wide limit
    db 0x0 ; 0x00 		<-  base high	1 byte							63 - 63

gdt_data: ;ds, ss, es , fs and gs should point to data descriptor. why??
    dw 0xffff ; 0xFFFF 2 bytes word
    dw 0x0 ; 0x0000 2 bytes word 
    db 0x0 ; 0x00 1 byte
    db 10010010b ; 0x92
    db 11001111b ; 0xCF
    db 0x0 ; 0x00 1 byte

gdt_end:

gdt_descriptor: ; there it goes the GDT descriptor 2 bytes size (8 null + 8 per entry = 24 = 0x18)
    dw gdt_end - gdt_start ; 0x18 (with calc, and debug) - Total GDT table size !!
    dd gdt_start ; 0x7c07

CODE_SEG equ gdt_code - gdt_start ; offste from start of the GDT to point CODE section ;0x8
DATA_SEG equ gdt_data - gdt_start ; offset from start of the GDT to point DATA section ;0x10

print:
    pusha
	xchg bx, bx ; magic breakpoint
    mov ah, 14
    mov bh, 0
.loop:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .loop
.done:
    popa
    ret

uzenet16 db 'Windows 9 !!!', 0
uzenet32 db 'uzenet32', 0

kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC ; byte 65,532 

    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, uzenet16
    call print

    cli
	xchg bx, bx ; magic breakpoint
	mov edx,0 ; Bringing to edx the values of the GDT calculations for analysis
	mov edx, gdt_start ; 0x7c07
	mov edx,0
	mov edx, gdt_end ; 0x7c1f
	mov edx,0
	mov edx, CODE_SEG ;0x8
	mov edx,0
	mov edx, DATA_SEG ;0x10
	mov edx,0
	mov edx, gdt_descriptor ; 0x7c1f
	mov edx,0
	mov edx, gdt_data ; 0x7c1f
	mov edx,0
	mov edx, gdt_code ; 0x7c0f

	xchg bx, bx ; magic breakpoint
    lgdt[gdt_descriptor] ; 0x7c1f
	xchg bx, bx ; magic breakpoint
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax
    jmp CODE_SEG:b32

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret

b32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov ebp, 0x2000
    mov esp, ebp

    mov ebx, uzenet32
    call print32

    jmp $

[SECTION signature start=0x7dfe]
dw 0AA55h
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Proctected mode and GDT question

Post by MichaelPetch »

jmp CODE_SEG:b32.Is FAR JMP. It sets both the segment and the offset. After setting the protected flag in CR0 you have to ensure that you are using a 32-bit code selector to be fully in 32-bit protected mode. Selector 0x08 happens to be defined in your code as a Code segment that is 32-bit. After CS is set with the FAR JMP you are now executing instructions as 32-bit. At that point you should be setting the other segment registers (DS, ES etc) to a 32-bit data selector.

The JMP after enabling the protected mode bit in CR0 also has the side effect of clearing the instruction prefetch queue. A CPU that may preload instructions ahead of time may have decoded instructions as 16-bit real mode and wouldn't have been aware that at some point the instructions had to be decoded as 32-bit code. The JMP clears the prefetch queue and forces the processor to start decoding instructions from scratch.
Last edited by MichaelPetch on Fri Oct 26, 2018 6:30 pm, edited 1 time in total.
mihe
Member
Member
Posts: 38
Joined: Sun Oct 21, 2018 1:37 pm

Re: Proctected mode and GDT question

Post by mihe »

MichaelPetch wrote:jmp CODE_SEG:b32.Is FAR JMP. It sets both the segment and the offset. After setting the protected flag in CR0 you have to ensure that you are using a 32-bit code selector to be fully in 32-bit protected mode. Selector 0x08 happens to be defined in your code as a Code segment that is 32-bit. After CS is set with the FAR JMP you are now executing instructions as 32-bit. At that point you should be setting the other segment registers (DS, ES etc) to a 32-bit data selector.

The JMP after enabling the protected mode big in CR0 also has the side effect of clearing the instruction prefetch queue. A CPU that may preload instructions ahead of time may have decoded instructions as 16-bit real mode and wouldn't have been aware that at some point the instructions had to be decoded as 32-bit code. The JMP clears the prefetch queue and forces the processor to start decoding instructions from scratch.
Thanks for the answer!, very insightful.

I have just realized what was wrong in my thought process. I was still thinking on segment * 16 + offset (real mode), but after the switch it is 16 bits selector + offset. So the selector 15-3 bits is the index (in my case 0x8, which is 0000_0000_0000_1000b), bit2=0 for GDT, and bit1-0 for ring level, in this case Ring 0.

Thanks again!
Post Reply