OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 2:00 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: Trying to make my very first GDT
PostPosted: Sun Nov 12, 2017 4:07 pm 
Offline

Joined: Sun Nov 05, 2017 1:46 pm
Posts: 13
I am trying to enter the protected mode without labels because I want to be sure that I understand how everything works. This is my code, that should print the characters from 'A' to 'F':
Code:
     1                                  %macro set_magic_number 0.nolist
     2                                     times 510 - ( $ - $$ ) db 0
     3                                     db 0x55, 0xAA
     4                                  %endmacro
     5                                 
     6                                  %macro print_c 1.nolist
     7                                     mov ah, 0x0E
     8                                     mov al, %1
     9                                     int 0x10
    10                                  %endmacro
    11                                 
    12 00000000 FA                      cli
    13                                 
    14 00000001 B8C007                  mov ax, 0x07C0
    15 00000004 8EC0                    mov es, ax
    16 00000006 260F01167D00            lgdt [es:0x007D]
    17 0000000C 0F20C0                  mov eax, cr0
    18 0000000F 0C01                    or al, 1
    19 00000011 0F22C0                  mov cr0, eax
    20 00000014 FB                      sti
    21 00000015 EA00000100              jmp 0x0001:0x0000
    22                                 
    23 0000001A B40EB030CD10            print_c '0'
    24 00000020 B40EB031CD10            print_c '1'
    25 00000026 B40EB032CD10            print_c '2'
    26 0000002C B40EB033CD10            print_c '3'
    27 00000032 B40EB034CD10            print_c '4'
    28 00000038 B40EB035CD10            print_c '5'
    29 0000003E B40EB036CD10            print_c '6'
    30 00000044 B40EB037CD10            print_c '7'
    31 0000004A B40EB038CD10            print_c '8'
    32 00000050 B40EB039CD10            print_c '9'
    33 00000056 B40EB041CD10            print_c 'A'
    34 0000005C B40EB042CD10            print_c 'B'
    35 00000062 B40EB043CD10            print_c 'C'
    36 00000068 B40EB044CD10            print_c 'D'
    37 0000006E B40EB045CD10            print_c 'E'
    38 00000074 B40EB046CD10            print_c 'F'
    39                                 
    40 0000007A FB                      sti
    41                                 
    42 0000007B EBFE                    stop:jmp stop
    43                                 
    44 0000007D 0010                    db 0x00,0x10
    45 0000007F 00007C7B                db 0x00,0x00,0x7C,0x83
    46                                 
    47 00000083 0000000000000000        dq 0x0
    48                                 
    49 0000008B FFFF                    dw 0xFF_FF
    50 0000008D 007C56                  db 0x00,0x7C,0x56
    51 00000090 4F                      db 0b01011111
    52 00000091 F0                      db 0b1111_0000
    53 00000092 00                      db 0x00
    54                                 
    55 00000093 00<rept>55AA            set_magic_number
In the lines from 14 to 19 I set up the GDT and start the protected mode.
In the line number 21 I make a far jump that should make the program print the caracters from 'A' to 'F'.
In the line 44 I put the size of the GDT. Each entry has a size of 8 bytes and I only have 2 entries: The null descriptor (because everybody recommends to put it) and the descriptor that I want to test. Total: 16 bytes (0x10)
In the line 45 I put the start location of the GDT: 0x7C00 (where the bootloader is loaded) + 0x0083 = 0x7C83
In the line 47 I set the null descriptor
In the line 49 I set the maximum size possible for a segment, as also did in line 52 with the four ones
In the line 50 I put the location where I would like to set the code segment: the location of the print_c 'A' instruction.
In the line 51 I set the access byte with the following values:
- bit 0: This bit tells us whether the segment is accessed by CPU, so it should be set to zero
- bit 1: Make the code segment read-only instead of unreadable
- bit 2: Not conforming. This way we don't need any privileges
- bit 3: Is a code segment, so it should be executable.
- bit 4: Reserved, always 1
- bit 5 and 6: 11 (3 in binary) because is the lowest privilege level.This way the segment is more accessible. Anyway, bit 2 make this bit useless.
- bit 7: Wheter the segment is present or not. I don't get it, but I set it to 1 because I think it should be 1 to be usable.
The first 4 bits in the line 52 are the size again, so I set it to 1111 because I want it to be the greatest possible so I don't have problems with the size (I'm just testing the GDT)
The last 4 bits in the line 52 I set the entry flag bits this way:
- bit 0 and 1: Reserved, always zero
- bit 2: size --> zero (16 bit mode)
- bit 3: granularity --> I don't want this, so zero.
And finally the line 53 with the last byte of the address of the segment.

However, this doesn't do anything. What I am missing?

_________________
Sorry if I commit some mistakes writting english. It is not my native language. If you find a typo in my posts, send me a PM


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Sun Nov 12, 2017 5:00 pm 
Offline
Member
Member

Joined: Thu May 19, 2011 5:13 am
Posts: 228
4dr14n31t0r wrote:
However, this doesn't do anything. What I am missing?
A simple way to access the real mode BIOS while in protected mode.

_________________
Mike Gonta
look and see - many look but few see

https://mikegonta.com


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Sun Nov 12, 2017 5:41 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
4dr14n31t0r wrote:
What I am missing?

Quite a few things.

You need an IDT before you can enable interrupts in protected mode. Segment selector 0x0001 is the null segment. Other than a handful of exceptions, you can't use the BIOS in protected mode. Every group of bytes you've written is backwards (x86 is little endian). Every group of bits you've written is backwards (x86 is insane but not that insane).

I suggest not wasting your time with 16-bit segments in protected mode. They're completely obsolete and useless on any CPU newer than the 286. I also suggest not wasting your time with nonzero segments in real mode and nonzero segment base addresses in protected mode.

mikegonta wrote:
A simple way to access the real mode BIOS while in protected mode.

That doesn't exist, so there's no point in wasting time trying to find it. (Anyone who claims otherwise doesn't understand it well enough.)


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Mon Nov 13, 2017 12:52 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
Code:
jmp 0x0001:0x0000
cannot be valid in protected mode. It is using the first (null) descriptor, which is invalid.


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Tue Nov 14, 2017 11:13 am 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1925
Location: Athens, GA, USA
Octocontrabass wrote:
4dr14n31t0r wrote:
What I am missing?

mikegonta wrote:
A simple way to access the real mode BIOS while in protected mode.

That doesn't exist, so there's no point in wasting time trying to find it. (Anyone who claims otherwise doesn't understand it well enough.)


I'm pretty sure Mike was being sarcastic, but this reply is still apropos because a novice wouldn't have the context to understand why it is a sarcastic answer.

It's a general problem with text-only communication channels, and is, among other things, a prime reason why emoticons (and now, emoji) exist.

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Tue Nov 14, 2017 6:03 pm 
Offline

Joined: Sun Nov 05, 2017 1:46 pm
Posts: 13
I am working very hard to get this protected mode working, but I'm still stuck. This is how my code looks like now:
Code:
     1                                  VGA_COLOR_LIGHT_RED     equ   0xC
     2                                  VGA_COLOR_LIGHT_GREEN   equ   0xA
     3                                 
     4                                  ;pos bg fg ch
     5                                  %macro write_c 4
     6                                     mov bx, 0x10
     7                                     mov es, bx
     8                                     mov bx, %1
     9                                     shl bx, 0x1
    10                                     mov   ah, %2
    11                                     shl   ah, 0x4
    12                                     or  ah, %3
    13                                     mov al, %4
    14                                     mov [es:bx], ax
    15                                  %endmacro
    16                                 
    17                                  %macro set_magic_number 0
    18                                     times 510 - ( $ - $$ ) db 0
    19                                     db 0x55, 0xAA
    20                                  %endmacro
    21                                 
    22                                  org 0x7C00
    23                                 
    24 00000000 FA                      cli
    25                                 
    26 00000001 0F0116[2C00]            lgdt [gdt_ptr]
    27                                 
    28 00000006 0F20C0                  mov eax, cr0
    29 00000009 0C01                    or al, 1
    30 0000000B 0F22C0                  mov cr0, eax
    31                                 
    32 0000000E EA[1300]0800            jmp 0x8:next
    33                                  next:
    34                                 
    35                                  write_c 0, VGA_COLOR_LIGHT_RED, VGA_COLOR_LIGHT_GREEN, 'X'
    36 00000013 BB1000              <1>  mov bx, 0x10
    37 00000016 8EC3                <1>  mov es, bx
    38 00000018 BB0000              <1>  mov bx, %1
    39 0000001B D1E3                <1>  shl bx, 0x1
    40 0000001D B40C                <1>  mov ah, %2
    41 0000001F C0E404              <1>  shl ah, 0x4
    42 00000022 80CC0A              <1>  or ah, %3
    43 00000025 B058                <1>  mov al, %4
    44 00000027 268907              <1>  mov [es:bx], ax
    45                                 
    46 0000002A EBFE                    stop: jmp stop
    47                                 
    48                                 
    49                                 
    50                                 
    51                                  gdt_ptr:
    52 0000002C 0017                       db 0,8*3-1
    53 0000002E 00007C32                   db 0,0,0x7C,0x32
    54                                 
    55                                  ; null descriptor
    56 00000032 0000000000000000           dq 0
    57                                 
    58                                  ; code descriptor
    59                                  ;      #0-------------- #1-------------- #2------ #3#4#5#6#7#8 #9#A-- #B#C#D#E#F------
    60                                  ;    0b1111111111111111_0000000000000000_00000000_0_1_0_1_1_00_1_1111_0_0_1_0_00000000
    61 0000003A FFFF00000059F200          db 0xff,0xff,         0x00,0x00,       0x00,    0x59,          0xf2,        0x00
    62                                 
    63                                  ; data descriptor:
    64                                  ;      #0-------------- #1-------------- #2------ #3#4#5#6#7#8 #9#A-- #B#C#D#E#F------
    65                                  ;    0b1111111111111111_0000101110000000_00000000_0_1_0_0_1_00_1_1111_0_0_1_0_00000000
    66 00000042 FFFF0B800049F200          db 0xff,0xff,         0x0b,0x80,       0x00,    0x49,          0xf2,        0x00
    67                                 
    68                                  set_magic_number
    69 0000004A 00<rept>            <1>  times 510 - ( $ - $$ ) db 0
    70 000001FE 55AA                <1>  db 0x55, 0xAA

I am not using interrupts so I don't need an IDT because I am writting to the screen though video memory (0x8B000).
In the line number 32 I make a far jump to 0x8:next to set the cs register with the second descriptor (Each descriptor's size is 8 bytes and the second is the code descriptor, so I think I should use 0x8).
In the line 16 I load 'es' with the value 0x10 because the data descriptor is the second descriptor and 0x8*2 = 0x10
This time I use db with commas instead of dw, dd or dq because I don't want to have problems with little endian.
These are now the values of the descriptors:
#0 & #A: Limit with the maximum possible value. This is a test program that is only supposed to print something in screen. I don't want problems with the size.
#1, #2 & #F: Base at the start of the memory.
#3: If the segment has been accessed or not. Therefore, zero
#4: Read/Write. I don't want to have problems so I set this to 1
#5: Direction/Conforming. Zero means no privilege level needed to use.
#6: Executable. This is only in the first case.
#7: Reserved, always 1
#8: Kernel privilege.
#9: Present. I want to use it. Therefore 1.
#B & #C: Reserved, always zero.
#D: Size. As Octocontrabass suggest I use 32 bit.
#E: Granularity. I think I don't need it.
I am spending a lot of time trying to find a way to get this working and I really would appreciate a minimal example that only have GDT and print something using it. This tutorial http://wiki.osdev.org/GDT_Tutorial use C code, but I want to do this in NASM.

_________________
Sorry if I commit some mistakes writting english. It is not my native language. If you find a typo in my posts, send me a PM


Last edited by 4dr14n31t0r on Wed Nov 15, 2017 2:08 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Tue Nov 14, 2017 8:03 pm 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
To sum up the errors: x86 is little Endian. Some of your structures and data have bytes in reverse order. You shouldn't have to hard code addresses like 0x7c32 into data structures. You can use labels for that. In your GDT you have the bytes in the correct order but the access byte and flags have all the bits in reverse order. You are also missing a bits 32 directive after the JMP so you end up with incorrect instruction encoding. Should look like:
Code:
jmp 0x8:next

bits 32
next:
You should be setting ES, DS, SS descriptors to 0x0010 and the base in the descriptor should be 0x00000000 not 0x000b8000 (I know what you are trying to do but it is very inefficient that way). The normal way makes a 4gb flat model and most x86 CPUs are optimized for the case where the base is 0x00000000. If you want to write to video memory just move with something like mov [b8000], 0x####. You can modify your write_c macro to compute the address and the attr/character at assembly time.

A version of your program that would likely work:
Code:
VGA_COLOR_LIGHT_RED     equ   0xC
VGA_COLOR_LIGHT_GREEN   equ   0xA

;pos bg fg ch
%macro write_c 4
mov word [0xb8000+(%1*2)],(%2<<12) | (%3<<8) | %4
%endmacro

%macro set_magic_number 0
times 510 - ( $ - $$ ) db 0
db 0x55, 0xAA
%endmacro

org 0x7C00

cli
lgdt [gdt_ptr]
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x8:next

bits 32
next:
mov eax, 0x10
mov es, ax        ; ES=DS=SS=0x10
mov ds, ax
mov ss, ax

write_c 0, VGA_COLOR_LIGHT_RED, VGA_COLOR_LIGHT_GREEN, 'X'
write_c 1, VGA_COLOR_LIGHT_RED, VGA_COLOR_LIGHT_GREEN, 'Y'
stop: jmp stop

gdt_ptr:
;db 0,8*3-1
;db 0,0,0x7C,0x32
dw endgdt-startgdt-1
dd startgdt

align 4
startgdt:
; null descriptor
dq 0
; code descriptor
;        #01-------------   #02-------------   #03-----   #04 #05 #06 #07 #08 #09  #10 #11-   #12 #13 #14 #15 #16-----
;      0b1111111111111111___0000000000000000___00000000___0___1___0___1___1___00___1___1111___0___0___1___0___00000000
db 0xff,0xff
db 0x00,0x00
db 0x00
db 0x9A
db 0xCF
db 0x00

; data descriptor:
;        #01-------------   #02-------------   #03-----   #04 #05 #06 #07 #08 #09  #10 #11-   #12 #13 #14 #15 #16-----
;      0b1111111111111111___0000101110000000___00000000___0___1___0___0___1___00___1___1111___0___0___1___0___00000000
db 0xff,0xff
; db 0x0b,0x80
db 0x00,0x00
db 0x00
db 0x92
db 0xCF
db 0x00
endgdt:

set_magic_number
In the code above the macro has something an LCP (Length Changing Prefix) stall because it uses an imm16 value in a move to a memory operand. It is cheaper (performance wise) to do the mov of an imm32 value into a 32-bit register and then move the lower 16-bit register into memory. The change would look like:
Code:
;pos bg fg ch
%macro write_c 4
mov eax, (%2<<12) | (%3<<8) | %4
mov word [0xb8000+(%1*2)], ax
%endmacro
On an unrelated note, you should consider making sure the A20 line is enabled. If you run it on hardware or a virtual machine where this isn't the case then memory addresses starting on an odd numbered megabyte boundary will not work as expected.


Last edited by MichaelPetch on Thu Nov 16, 2017 4:23 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Thu Nov 16, 2017 2:35 pm 
Offline

Joined: Sun Nov 05, 2017 1:46 pm
Posts: 13
MichaelPetch wrote:
To sum up the errors: x86 is little Endian
Ok, I get it. But I still don't get something:
This wiki(http://wiki.osdev.org/Global_Descriptor_Table) says that bit 0 of access byte is the accessed bit and bit 7 is the present bit. 0 is smaller than 7, so it goes before. But you say that the bits are in reverse order. WTF?
Here is the image:
Image
And I have a theory that I would like to confirm: In the following image the bits from 0 to 16 are the limit. Is that also in little endian? For example, if I want the limit to be 0xFFF0 (65520 in decimal), and the start of the descriptor is, let say, at 0x0800 (for example), How would the memory at that location be filled?
a) 0x0800 --> 0xFF; 0x0801 --> 0xF0
b) 0x0800 --> 0xF0; 0x0801 --> 0xFF
Image
The image clearly shows that the limit is the bits from 0 to 15 in memory, but doesn't specify if those 2 bytes should be in little endian or not. I know that the x86 works with little endian, but I don't know how computer internally accesses that memory. I think that this could sound kinda stupid, but if I access the limit byte by byte then the limit would be in big endian. I think so because I see a separate byte of limit in bits 48-51 and I don't know if the computer first take the word from bits 0-15 and the byte from 48-51 and then do some operations to get the Limit bytes in the correct order or just obtain the entire limit byte by byte to easily obtain it in the correct order. I think I am overthinking this simple concept.

Something similar happens to me with the jmp instruction: I know that I shouldn't be hardcoding the location of the jmp instruction, but when I do something like:
Code:
jmp ABCD
I have to see how NASM internally assembles that instruction because I don't know if NASM think that ABCD is the location where I want to jump without little endian conversion and should make that conversion for me, or if I already considered the little endian and only have to put that number in the output binary file as is.

_________________
Sorry if I commit some mistakes writting english. It is not my native language. If you find a typo in my posts, send me a PM


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Thu Nov 16, 2017 2:54 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
I think that you perhaps need to brush up on your understanding of the processor and assembler coding before proceeding further.

Try assembling some simple programs, and run them under a debugger such as gdb. Examine the memory locations and registers, and see how they relate to the instructions and change as the instructions are executed.

Without a thorough understanding of the underlying hardware, and how it works in practice, you are just going to flounder with the more complicated stuff.


Top
 Profile  
 
 Post subject: Re: Trying to make my very first GDT
PostPosted: Sun Nov 19, 2017 10:46 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
4dr14n31t0r wrote:
This wiki(http://wiki.osdev.org/Global_Descriptor_Table) says that bit 0 of access byte is the accessed bit and bit 7 is the present bit. 0 is smaller than 7, so it goes before. But you say that the bits are in reverse order. WTF?

There's bit order, and then there's byte order.

Bits are always written in the same order. The most-significant bit is on the left and the least-significant bit is on the right. This matches how numbers are written in base 10, so to keep things simple we write numbers the same way in every other base system too.

Bits are not always numbered the same way. Intel has decided that the least-significant bit is bit 0, and any bits of greater significance are numbered from there. So, for example, a constant with only bit 7 set would be written 0b10000000 or 0x80, and a constant with only bit 15 set would be written 0b10000000_00000000 or 0x8000.

Bytes are not always written in the same order. Intel says the least-significant byte goes at the lowest memory address, which means in your code you have to put the byte containing bit 0 first. Your assembler will do this automatically when you write a value that takes more than one byte; for example, "dw 0x1234" is equivalent to "db 0x34,0x12".

Unless otherwise specified, you should assume for everything x86 that bit 0 is the least-significant bit and the least-significant byte is at the lowest address.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot], SemrushBot [Bot] and 60 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group