2

我目前正在为我的引导加载程序设置 GDT。我有 3 (4) 段:

  • (零段)
  • 4GB 内核代码段
  • 4GB 内核数据段
  • 2GB 堆栈数据部分(我在截屏时忘记将 1 位设置为 0。稍后这将是 1mb)

这是我设置 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

但是当我将二进制文件加载到 bochs 中时,它给了我以下结果: GDT 图像

字节完全按照我定义的方式加载到内存中: 记忆影像

在这里,我意识到每次都会将 0xFFF 添加到段中。这是因为我使用 4kb 作为粒度吗?

当我选择 0xFF 作为 4kb 粒度的大小时,这将扩展为 0xFFFFF,所以我只能使段 1mb - 1 字节大吗?

4

1 回答 1

3

是的,这是因为您在 GDT 条目中使用了 4kb 粒度。来自英特尔® 64 和 IA-32 架构开发人员手册:卷。3A关于5.3 限制检查

当 G 标志清零时(字节粒度),有效限制是段描述符中 20 位限制字段的值。这里,限制范围从 0 到 FFFFFH (1 MByte)。当设置 G 标志(4-KByte 页面粒度)时,处理器将 limit 字段中的值缩放 2^12 (4 KBytes) 的因子。在这种情况下,有效限制范围从 FFFH (4 KBytes) 到 FFFFFFFFH (4 GBytes)。请注意,当使用缩放时(设置了 G 标志),不检查段偏移量(地址)的低 12 位是否符合限制;例如,请注意,如果段限制为 0,则偏移量 0 到 FFFH 仍然有效。

来自英特尔® 64 和 IA-32 架构开发人员手册:卷。2A LSL指令描述了所使用的机制并描述了您在 BOCHS 中看到的行为:

段限制是包含在字节 0 和 1 以及段描述符的字节 6 的前 4 位中的 20 位值。如果描述符具有字节粒度段限制(粒度标志设置为 0),则目标操作数加载字节粒度值(字节限制)。如果描述符具有页粒度段限制(粒度标志设置为 1),则 LSL 指令会在将页粒度限制(页限制)加载到目标操作数之前将其转换为字节限制。通过将 20 位“原始”限制左移 12 位并用 1 填充低 12 位来执行转换。

你问了一个问题,当我选择 0xFF 作为 4kb 粒度的大小时,它会扩展到 0xFFFFF,所以我只能使段 1mb - 1 字节大吗?. 对于页粒度,0xFF 段限制向左移动 12 位,产生 0xFF000,低 12 位设置为 1。结果是 0xFFFFF 字节的实际限制。此限制允许从指定的基址开始寻址 0 到 0xFFFFF(含)的完整 1MiB 内存。如果您希望段具有特定的字节大小(在 0x00000 和 0xFFFFF 之间),您可以使用字节粒度。可以用不同的粒度定义描述符。

于 2019-05-03T13:07:38.640 回答