细心的读者会发现前面的GDT 试验中,设置的选择子和实际查看到的选择子有着1bit的差别。比如:
//
// LINEAR_CODE64_SEL
//
{
0x0FFFF, // limit 15:0
0x0, // base 15:0
0x0, // base 23:16
0x09A, // present, ring 0, code, execute/read
0x0AF, // page-granular, 64-bit code
0x0, // base (high)
},
实际读取到的是:
No.[7] Seg. Desc 0xAF9B000000FFFF
Base=0x0
Limit=0xFFFFF
Descriptor Type: Code or Data
64-bit code segment
设置的是 9A 读取到的是 9B,二者相差1Bit。
对于这个问题开始研究。
首先确定差别的位置:
typedef struct _GDT_ENTRY {
UINT16 Limit15_0;
UINT16 Base15_0;
UINT8 Base23_16;
UINT8 Type;
UINT8 Limit19_16_and_flags;
UINT8 Base31_24;
} GDT_ENTRY;
是 Type 中有差别。结合【参考1】资料:
差别就是上图中 Type A Bit. 表示当前的是否被访问过。没有访问过就是 0,访问过就是1.
接下来通过一个实验来验证上面的知识。首先,选择一个 selector,我这里用的是下面这个
//
// LINEAR_SEL
//
{
0x0FFFF, // limit 15:0
0x0, // base 15:0
0x0, // base 23:16
0x092, // present, ring 0, data, read/write
0x0CF, // page-granular, 32-bit
0x0,
},
最主要是直接使用这个我们就不用重新编译BIOS或者再写一个加载描述符的Application了。
其次,加载 fs 的代码如下:
[BITS 64]
mov ecx,28h
mov fs,cx
用Nasm 转化为机器码对应如下:
1 [BITS 64]
2 00000000 B928000000 mov ecx,8h
3 00000005 8EE1 mov fs,cx
使用之前的代码,我们写一个 LoadFS.efi代码如下:
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/IoLib.h>
extern EFI_SYSTEM_TABLE *gST;
extern EFI_BOOT_SERVICES *gBS;
void
SetFS()
{
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
}
/***
Print a welcoming message.
Establishes the main structure of the application.
@retval 0 The application exited normally.
@retval Other An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
UINT8 *f=(UINT8 *)&SetFS;
*(f+0)=0xB9;
*(f+1)=0x08;
*(f+2)=0x00;
*(f+3)=0x00;
*(f+4)=0x00;
*(f+5)=0x8E;
*(f+6)=0xE1;
SetFS();
return(0);
}
编译后就可以进行实验了,实验必须在实体机上进行。
1.启动到Shell 后先用 gdt.efi 查看一下
GDTR=0x8C634718
No.[0] Seg. Desc 0x0
Base=0x0
Limit=0x0
Descriptor Type: system
Not 64-bit code segment
No.[1] Seg. Desc 0xCF92000000FFFF
Base=0x0
Limit=0xFFFFF
Descriptor Type: Code or Data
Not 64-bit code segment
(后面省略)
注意 92 那个位置
2.运行 loadfs.efi
3.再次运行 gdt.efi 进行查看
GDTR=0x8C634718
No.[0] Seg. Desc 0x0
Base=0x0
Limit=0x0
Descriptor Type: system
Not 64-bit code segment
No.[1] Seg. Desc 0xCF93000000FFFF
Base=0x0
Limit=0xFFFFF
Descriptor Type: Code or Data
Not 64-bit code segment
(后面省略)
可以看到已经变成了93h,就是说如果 selector被加载到某个段寄存器,对应的 selector上的 type accessed 位将会做出标记。
完整代码下载:
参考:
1.” Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4” Figure 5-1. Descriptor Fields Used for Protection P2855