assemblyx86osdevbochsgdt

Bochs GDT Segment limit is shifted left 3 times in hex and 0xFFF is added. Is this normal?


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: GDT Image

The Bytes loaded exactly as i defined them into memory: Memory Image

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?


Solution

  • 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.