细心的读者会发现前面的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】资料:

【参考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

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>