Im currently setting up a GDT for my bootloader. I have 3 (4) segments:
Here is my Code for setting up the GDT:
7 ; GDT null segment
8 gdt_null:
9 dq 0x00
10
11 ; GDT code segment (4GB)
12 gdt_code:
13 dw 0xFFFF
14 dw 0x00
15 db 0x00
16 db 10011010b
17 db 11001111b
18 db 0x00
19
20 ; GDT data segment (4GB)
21 gdt_data:
22 dw 0xFFFF
23 dw 0x00
24 db 0x00
25 db 10010010b
26 db 11001111b
27 db 0x00
28
29 ; Extra segmet for stack
30 ; Preventing bufferoverflows from stack could write into other data
31 ; Size is 1M (0x100 * 4kb) startting from base 7e00 (512 bytes after 7c00)
32 gdt_stack:
33 dw 0x0100
34 dw 0x7e00
35 db 0x00
36 db 10010010b
37 db 11001000b
38 db 0x00
But when I loading the binary into bochs it gives me following result:
The Bytes loaded exactly as i defined them into memory:
Here i realised that 0xFFF is added every time to a segment. Is this because I used 4kb as granularity?
When I choose 0xFF as size with 4kb granularity will be this extended to 0xFFFFF, so i can only make the segment 1mb - 1 byte big?
Yes, it is because you use 4kb granularity in the GDT entry. From the Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 3A regarding 5.3 Limit Checking:
When the G flag is clear (byte granularity), the effective limit is the value of the 20-bit limit field in the segment descriptor. Here, the limit ranges from 0 to FFFFFH (1 MByte). When the G flag is set (4-KByte page granularity), the processor scales the value in the limit field by a factor of 2^12 (4 KBytes). In this case, the effective limit ranges from FFFH (4 KBytes) to FFFFFFFFH (4 GBytes). Note that when scaling is used (G flag is set), the lower 12 bits of a segment offset (address) are not checked against the limit; for example, note that if the segment limit is 0, offsets 0 through FFFH are still valid.
From the Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 2A the LSL instruction has a description of the mechanism used and describes the behavior you are seeing in BOCHS:
The segment limit is a 20-bit value contained in bytes 0 and 1 and in the first 4 bits of byte 6 of the segment descriptor. If the descriptor has a byte granular segment limit (the granularity flag is set to 0), the destination operand is loaded with a byte granular value (byte limit). If the descriptor has a page granular segment limit (the granularity flag is set to 1), the LSL instruction will translate the page granular limit (page limit) into a byte limit before loading it into the destination operand. The translation is performed by shifting the 20-bit “raw” limit left 12 bits and filling the low-order 12 bits with 1s.
You asked the question When I choose 0xFF as size with 4kb granularity will be this extended to 0xFFFFF, so i can only make the segment 1mb - 1 byte big? . With page granularity the 0xFF segment limit is shifted 12 bits left yielding 0xFF000 and the low 12 bits are set to 1. The result is an actual limit of 0xFFFFF bytes. This limit allows a full 1MiB of memory to be addressed which is 0 to 0xFFFFF (inclusive) from the specified base. If you want a segment to have a specific size in bytes (between 0x00000 and 0xFFFFF) you can use byte granularity. It is possible to have descriptors defined with different granularity.