15 Global Descriptor Table

16 bit mode 是以 segment based 的方式 access memory。32 bit mode 將 logic address 轉成 physical address 的方式跟 16 bit mode 不同。在 32 bit mode 下,segment register 的值代表的是 global descriptor table 中某個 segment descriptor 的 index。

segment descriptor 是個 8 byte 的 structure,包含以下資訊:

  • base address (32 bits):定義 segment 從 physical memory 的起始位置
  • segment limit (20 bit):segment 的 size
  • flags:這些 flag 會影響 CPU 如何解讀 segment,例如 privilige level 跟這塊 segment 是不是 read-only 或 write-only 等。

下圖是 segment descriptor 的結構:

最簡單的 segment configuration 稱為 basic flat mode,是由兩個重疊的 segment 組成,一個是給 code 使用,另一個是放 data。這兩個 segment 會 cover 整個 4GB 的 memory。那也因為這兩個 segment 位置是重疊的,當然不可能有什麼保護機制(保護一個 segment 不被另一個 segment 亂搞之類的)。

GDT 的 descriptor entry 就是用來描述每個 segment 的。除了 code 跟 data segment 之外,GDT 的第一個 descriptor 必須要是 null descriptor,內容是 8 byte 的 0。這是在轉換到 protcted mode 時,如果不小心忘記設定 segment register,會讓我們剛好 access 到 null descriptor。而 CPU 在 access 到 null descriptor 的時候會 raise exception(interrupt)讓我們知道有錯誤。

code segment 的 descriptor configuration:

  • Base:0x0
  • Limit:0xffff
  • Present:1,表示這個 segment 是 present 在 memory 中。這個 config 之後會用於 virtual memory。
  • Privilege:0,ring 0 是最高 privilige
  • Descriptor type:1,code 或 data segment
  • Type:
    • Code:1,因為這是 code segment
    • Confirming:0,比較低 privilege 的 segment 不能 call 這個 segment 的 code,這是 memory protection 的關鍵
    • Readable:1,1 為 readable,0 為 execute only
    • Accessed:0,這個參數通常用在 virtual memory 的 debug
  • Other flags:
    • Granularity:1,這會將 Limit 乘以 4K(16 * 16 * 16),所以 0xffff 的 limit 會變成 0xffff000
    • 32-bit default:1,因為我們的 segment 會放 32-bit code。0 則是 16-bit。這實際上是設定 operation 的 default size,例如 push 0x4 到 stack 的時候,會自動被擴展成 32-bit 的數字。
    • 64-bit code segment:0,32-bit CPU 不會用到
    • AVL:0,這是可以設給自己使用的 bit

因為我們的 data segment 跟 code segment 位置是重疊的,所以除了以下的 type flag 之外,data segment 的 configuration 跟 code segment 是一樣的:

  • Code:0
  • Expand down:0
  • Writable:1
  • Accessed:0

Define the GDT in Assembly

我們會用 dbdwdd 這幾個指令來放 bytes、建立 GDT。

CPU 需要知道 GDT 有多長,我們會用 GDT descriptor 來描述 GDT,GDT descriptor 包含:

  • GDT size (16 bit)
  • GDT address (32 bit)
gdt_start:

gdt_null: ; the mandatory null descriptor
dd 0x0 ; dd means define double word (4 bytes)
dd 0x0

gdt_code: ; the code segment descriptor
; base = 0x0, limit = 0xfffff
; 1st flags: (present)1 (privilege)00 (descriptor type)1 -> 1001b
; type flags: (code)1 (conforming)0 (readable)1 (accessed)0 -> 1010b
; 2nd flags: (granularity)1 (32-bit default)1 (64-bit seg)0 (AVL)0 -> 1100
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 0-15)
db 10011010b ; 1st flags, type flags
db 11001111b ; 2nd flags, Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)

gdt_data: ; the data segment descriptor
; Same as code segment except for the type flags:
; type flags: (code)0 (expand down)0 (writable)1 (accessed)0 -> 0010b
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 0-15)
db 10010010b ; 1st flags, type flags
db 11001111b ; 2nd flags, Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)

gdt_end:

; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; Size of GDT, always less one of the true size
dd gdt_start ; Start address of GDT

; segment descriptor offset
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start